diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs index 0fbc3ee8b7b11..8f4c31390db47 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/DefinitionMap.cs @@ -311,10 +311,16 @@ internal VariableSlotAllocator TryCreateVariableSlotAllocator(EmitBaseline basel symbolMap = MapToMetadataSymbolMatcher; } + var previousMethod = mappedMethod.PreviousMethod; + if (previousMethod is null) + { + previousMethod = (IMethodSymbolInternal)symbolMap.MapDefinitionOrNamespace(topLevelMethod); + } + return new EncVariableSlotAllocator( symbolMap, mappedMethod.SyntaxMap, - mappedMethod.PreviousMethod, + previousMethod, methodId, previousLocals, lambdaMap, diff --git a/src/Compilers/Core/Portable/Emit/SemanticEdit.cs b/src/Compilers/Core/Portable/Emit/SemanticEdit.cs index 6e94ee5e55467..caea2c3ce8e91 100644 --- a/src/Compilers/Core/Portable/Emit/SemanticEdit.cs +++ b/src/Compilers/Core/Portable/Emit/SemanticEdit.cs @@ -53,7 +53,7 @@ public struct SemanticEdit : IEquatable /// /// The type of edit. /// - /// The symbol from the earlier compilation, or null if the edit represents an addition. + /// The symbol from the earlier compilation, or null if the edit represents an addition or an update of the symbol from the previous compilation that exactly matches . /// /// /// The symbol from the later compilation, or null if the edit represents a deletion. @@ -74,7 +74,7 @@ public struct SemanticEdit : IEquatable /// public SemanticEdit(SemanticEditKind kind, ISymbol? oldSymbol, ISymbol? newSymbol, Func? syntaxMap = null, bool preserveLocalVariables = false) { - if (oldSymbol == null && kind != SemanticEditKind.Insert) + if (oldSymbol == null && kind is not (SemanticEditKind.Insert or SemanticEditKind.Update)) { throw new ArgumentNullException(nameof(oldSymbol)); } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs index ac43c9300e32e..46d7151fa5d2d 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs @@ -7,11 +7,13 @@ using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class ActiveStatementTests_Methods : EditingTestBase { #region Methods @@ -43,12 +45,17 @@ static void Main(string[] args) } "; - // TODO (bug 755959): better deleted active statement span var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); + EditAndContinueValidation.VerifySemantics( + new[] { edits }, + new[] + { + DocumentResults( + active, + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(int)")) }) + }); } [Fact] @@ -550,7 +557,7 @@ class C var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "get")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "get", FeaturesResources.code)); } [Fact] @@ -575,7 +582,7 @@ public void Property_BlockBodyToExpressionBody2() var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "int P", CSharpFeaturesResources.property_setter)); + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_setter, "P.set"))); } [Fact] @@ -601,7 +608,7 @@ class C // Can be improved with https://github.com/dotnet/roslyn/issues/22696 edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "int P")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "int P", FeaturesResources.code)); } #endregion @@ -654,7 +661,7 @@ public void Indexer_BlockBodyToExpressionBody2() var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "int this[int a]", CSharpFeaturesResources.indexer_setter)); + Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); } [Fact] @@ -756,9 +763,10 @@ public T this[int i] var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); + // Rude edits of active statements (AS:1) are not reported if the top-level edits are rude. edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ActiveStatementUpdate, @"stringCollection[1] = ""hello"";"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "set", CSharpFeaturesResources.indexer_setter)); + Diagnostic(RudeEditKind.GenericTypeUpdate, "set", CSharpFeaturesResources.indexer_setter), + Diagnostic(RudeEditKind.ActiveStatementUpdate, "stringCollection[1] = \"hello\";")); } [Fact] @@ -858,9 +866,10 @@ public T this[int i] var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); + // Rude edits of active statements (AS:1) are not reported if the top-level edits are rude. edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(stringCollection[1]);"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "get", CSharpFeaturesResources.indexer_getter)); + Diagnostic(RudeEditKind.GenericTypeUpdate, "get", CSharpFeaturesResources.indexer_getter), + Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(stringCollection[1]);")); } [Fact] @@ -959,7 +968,7 @@ public T this[int i] var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } [Fact] @@ -1058,7 +1067,7 @@ public T this[int i] var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } #endregion diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index 2fc405947deb6..3f5d01ca493bf 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -9,11 +9,15 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.CSharp.UnitTests; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class ActiveStatementTests : EditingTestBase { #region Update @@ -240,7 +244,7 @@ static void Goo(int a) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } // TODO (tomat): considering a change @@ -365,20 +369,20 @@ static void Goo(int a) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "{"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "{"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "case 2:"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "default:"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "{"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "{"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "while (true)"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "do"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "for (int i = 0; i < 10; i++ )"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "foreach (var i in new[] { 1, 2 })"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "using ( var z = new C() )"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "fixed ( char* p = \"s\" )"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "label")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "case 2:", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "default:", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "while (true)", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "do", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "for (int i = 0; i < 10; i++ )", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "foreach (var i in new[] { 1, 2 })", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "using ( var z = new C() )", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "fixed ( char* p = \"s\" )", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "label", FeaturesResources.code)); } [Fact] @@ -588,7 +592,7 @@ static void Goo(int a) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } [WorkItem(755959, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755959")] @@ -648,7 +652,8 @@ static void Main(String[] args) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, null, FeaturesResources.namespace_)); + Diagnostic(RudeEditKind.Delete, null, FeaturesResources.namespace_), + Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.class_, "N.C"))); } #endregion @@ -1310,6 +1315,19 @@ class C edits.VerifyRudeDiagnostics(active); } + [Fact] + public void InstanceConstructor_DeleteParameterless() + { + var src1 = "partial class C { public C() { System.Console.WriteLine(1); } }"; + var src2 = "partial class C { }"; + + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.DeleteActiveStatement, "partial class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); + } + #endregion #region Field and Property Initializers @@ -2087,8 +2105,8 @@ public C() {} var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.field)); + edits.VerifySemanticDiagnostics(active, + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))); } [Fact] @@ -2221,9 +2239,9 @@ public C() {} var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, + edits.VerifySemanticDiagnostics(active, Diagnostic(RudeEditKind.Move, "int c", FeaturesResources.field), - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.field)); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))); } [Fact] @@ -2272,8 +2290,8 @@ class C var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.field)); + edits.VerifySemanticDiagnostics(active, + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))); } [Fact] @@ -2294,8 +2312,8 @@ class C var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.auto_property)); + edits.VerifySemanticDiagnostics(active, + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.auto_property, "a"))); } #endregion @@ -4292,7 +4310,7 @@ static void Main(string[] args) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "for (; i < 10 ; i++)")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "for (; i < 10 ; i++)", FeaturesResources.code)); } [Fact] @@ -4470,7 +4488,7 @@ static void Main(string[] args) var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "for (int i = 1; ; i++ )")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "for (int i = 1; ; i++ )", FeaturesResources.code)); } [Fact] @@ -6077,12 +6095,12 @@ class C { public static int Main() { - return F() switch + return F() switch { int a when F1() => F2(), bool b => F3(), _ => F4() - }; + }; } }"; @@ -6172,18 +6190,17 @@ public void SwitchExpression_NestedInGoverningExpression() var src1 = @" class C { - public static int Main() => (F() switch { 0 => 1, _ => 2 }) switch { 1 => 10, _ => 20 }; + public static int Main() => (F() switch { 0 => 1, _ => 2 }) switch { 1 => 10, _ => 20 }; }"; var src2 = @" class C { - public static int Main() => (G() switch { 0 => 10, _ => 20 }) switch { 10 => 100, _ => 200 }; + public static int Main() => (G() switch { 0 => 10, _ => 20 }) switch { 10 => 100, _ => 200 }; }"; var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ActiveStatementUpdate, "switch { 0 => 10, _ => 20 }"), Diagnostic(RudeEditKind.SwitchExpressionUpdate, "switch", FeaturesResources.method), Diagnostic(RudeEditKind.SwitchExpressionUpdate, "switch", FeaturesResources.method)); } @@ -6704,7 +6721,7 @@ static void Main() var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } [Fact] @@ -6786,7 +6803,7 @@ static void Main() var active = GetActiveStatements(src1, src2); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } [Fact] @@ -8488,18 +8505,20 @@ static void Main(string[] args) public void Lambdas_ExpressionToDelegate() { var src1 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = a => 1; } } "; var src2 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = delegate(int a) { return 1; }; } @@ -8542,18 +8561,20 @@ static void Main(string[] args) public void Lambdas_DelegateToExpression() { var src1 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = delegate(int a) { return 1; }; } } "; var src2 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = a => 1; } @@ -8569,18 +8590,20 @@ static void Main(string[] args) public void Lambdas_StatementsToDelegate() { var src1 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = a => { return 1; }; } } "; var src2 = @" +using System; class C { - static void Main(string[] args) + static void Main() { Func f = delegate(int a) { return 2; }; } @@ -9547,6 +9570,9 @@ static async void F() public void LambdaToAsyncLambda_WithActiveStatement() { var src1 = @" +using System; +using System.Threading.Tasks; + class C { static void F() @@ -9560,6 +9586,9 @@ static void F() } "; var src2 = @" +using System; +using System.Threading.Tasks; + class C { static void F() @@ -9583,6 +9612,8 @@ static void F() public void LambdaToAsyncLambda_WithActiveStatement_NoAwait() { var src1 = @" +using System; + class C { static void F() @@ -9592,6 +9623,8 @@ static void F() } "; var src2 = @" +using System; + class C { static void F() @@ -9705,20 +9738,24 @@ static void F() public void AnonymousFunctionToAsyncAnonymousFunction_WithActiveStatement_NoAwait() { var src1 = @" +using System.Threading.Tasks; + class C { static void F() { - var f = new Action(delegate() { Console.WriteLine(1); }); + var f = new Func(delegate() { Console.WriteLine(1); return Task.CompletedTask; }); } } "; var src2 = @" +using System.Threading.Tasks; + class C { static async void F() { - var f = new Action(async delegate() { Console.WriteLine(1); }); + var f = new Func(async delegate() { Console.WriteLine(1); }); } } "; @@ -10495,6 +10532,62 @@ static void F() #endregion + #region Partial Types + + [Fact] + public void InsertDeleteMethod_Inactive() + { + // Moving inactive method declaration in a file with active statements. + + var srcA1 = "partial class C { void F1() { System.Console.WriteLine(1); } }"; + var srcB1 = "partial class C { void F2() { } }"; + var srcA2 = "partial class C { void F1() { System.Console.WriteLine(1); } void F2() { } }"; + var srcB2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + activeStatements: GetActiveStatements(srcA1, srcA2), + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F2")), + }), + DocumentResults( + activeStatements: GetActiveStatements(srcB1, srcB2)) + }); + } + + [Fact, WorkItem(51177, "https://github.com/dotnet/roslyn/issues/51177")] + public void InsertDeleteMethod_Active() + { + // Moving active method declaration in a file with active statements. + // TODO: this is currently a rude edit + + var srcA1 = "partial class C { }"; + var srcB1 = "partial class C { void F() { System.Console.WriteLine(1); } }"; + var srcA2 = "partial class C { void F() { System.Console.WriteLine(1); } }"; + var srcB2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + activeStatements: GetActiveStatements(srcA1, srcA2), + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F")), + }), + DocumentResults( + activeStatements: GetActiveStatements(srcB1, srcB2), + diagnostics: new[] { Diagnostic(RudeEditKind.DeleteActiveStatement, "partial class C", DeletedSymbolDisplay(FeaturesResources.method, "F()")) }) + }); + } + + #endregion + #region Misc [Fact] @@ -10516,8 +10609,8 @@ static void Goo(int a) var src2 = @""; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, null, FeaturesResources.class_)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.class_, "C"))); } [Fact] @@ -10587,7 +10680,7 @@ public static void F() active.OldStatements[0] = active.OldStatements[0].WithFlags(ActiveStatementFlags.PartiallyExecuted | ActiveStatementFlags.IsLeafFrame); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementDelete, "{")); + Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementDelete, "{", FeaturesResources.code)); } [Fact] @@ -10614,7 +10707,7 @@ public static void F() active.OldStatements[0] = active.OldStatements[0].WithFlags(ActiveStatementFlags.IsNonLeafFrame | ActiveStatementFlags.IsLeafFrame); edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "{")); + Diagnostic(RudeEditKind.DeleteActiveStatement, "{", FeaturesResources.code)); } [Fact] @@ -10690,7 +10783,7 @@ public static void H(int x) var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - var validator = CSharpEditAndContinueTestHelpers.CreateInstance(node => + var validator = new CSharpEditAndContinueTestHelpers(faultInjector: node => { if (node.Parent is MethodDeclarationSyntax methodDecl && methodDecl.Identifier.Text == "G") { @@ -10702,7 +10795,10 @@ public static void H(int x) Diagnostic(RudeEditKind.MemberBodyTooBig, "public static void G()", FeaturesResources.method) : Diagnostic(RudeEditKind.MemberBodyInternalError, "public static void G()", FeaturesResources.method); - validator.VerifyRudeDiagnostics(edits, active, new[] { expectedDiagnostic }); + validator.VerifySemantics( + new[] { edits }, + TargetFramework.NetCoreApp, + new[] { DocumentResults(diagnostics: new[] { expectedDiagnostic }) }); } #endregion diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index e1d2f3da6f9ce..a6f68489a40e9 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -292,8 +292,8 @@ public static void Main() var result = await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray.Empty, CancellationToken.None); Assert.True(result.HasChanges); - Assert.True(result.SemanticEdits[0].PreserveLocalVariables); var syntaxMap = result.SemanticEdits[0].SyntaxMap; + Assert.NotNull(syntaxMap); var newStatementSpan = result.ActiveStatements[0].Span; var newStatementTextSpan = newText.Lines.GetTextSpan(newStatementSpan); @@ -685,13 +685,16 @@ public static void Main() } "; var source2 = @" +class D +{ +} "; using var workspace = TestWorkspace.CreateCSharp(source1, composition: s_composition); - var oldProject = workspace.CurrentSolution.Projects.Single(); - var newDocId = DocumentId.CreateNewId(oldProject.Id); var oldSolution = workspace.CurrentSolution; + var oldProject = oldSolution.Projects.Single(); + var newDocId = DocumentId.CreateNewId(oldProject.Id); var newSolution = oldSolution.AddDocument(newDocId, "goo.cs", SourceText.From(source2)); workspace.TryApplyChanges(newSolution); @@ -715,8 +718,7 @@ public static void Main() } Assert.True(result.IsSingle()); - Assert.Equal(1, result.Single().RudeEditErrors.Count()); - Assert.Equal(RudeEditKind.InsertFile, result.Single().RudeEditErrors.Single().Kind); + Assert.Empty(result.Single().RudeEditErrors); } [Theory, CombinatorialData] diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs index 802f37365d791..206cbef8f3b8c 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestHelpers.cs @@ -5,39 +5,28 @@ #nullable disable using System; -using System.Collections.Generic; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Text; -using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { internal sealed class CSharpEditAndContinueTestHelpers : EditAndContinueTestHelpers { - private readonly ImmutableArray _fxReferences; private readonly CSharpEditAndContinueAnalyzer _analyzer; - public CSharpEditAndContinueTestHelpers(TargetFramework targetFramework, Action faultInjector = null) + public CSharpEditAndContinueTestHelpers(Action faultInjector = null) { - _fxReferences = TargetFrameworkUtil.GetReferences(targetFramework); _analyzer = new CSharpEditAndContinueAnalyzer(faultInjector); } - internal static CSharpEditAndContinueTestHelpers CreateInstance(Action faultInjector = null) - => new CSharpEditAndContinueTestHelpers(TargetFramework.Mscorlib46Extended, faultInjector); - - internal static CSharpEditAndContinueTestHelpers CreateInstance40(Action faultInjector = null) - => new CSharpEditAndContinueTestHelpers(TargetFramework.Mscorlib40AndSystemCore, faultInjector); - public override AbstractEditAndContinueAnalyzer Analyzer => _analyzer; - - public override Compilation CreateLibraryCompilation(string name, IEnumerable trees) - => CSharpCompilation.Create("New", trees, _fxReferences, TestOptions.UnsafeReleaseDll); + public override string LanguageName => LanguageNames.CSharp; + public override TreeComparer TopSyntaxComparer => SyntaxComparer.TopLevel; public override SyntaxTree ParseText(string source) => SyntaxFactory.ParseSyntaxTree(source, CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs index f4f6a74a1bea7..b45ad8c484eaa 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditAndContinueValidation.cs @@ -21,7 +21,7 @@ internal static void VerifyUnchangedDocument( string source, ActiveStatementsDescription description) { - CSharpEditAndContinueTestHelpers.CreateInstance().VerifyUnchangedDocument( + new CSharpEditAndContinueTestHelpers().VerifyUnchangedDocument( ActiveStatementsDescription.ClearTags(source), description.OldStatements, description.NewSpans, @@ -40,7 +40,7 @@ internal static void VerifyRudeDiagnostics( ActiveStatementsDescription description, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - CSharpEditAndContinueTestHelpers.CreateInstance().VerifyRudeDiagnostics( + VerifySemanticDiagnostics( editScript, description, expectedDiagnostics); @@ -52,7 +52,7 @@ internal static void VerifyLineEdits( IEnumerable expectedNodeUpdates, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - CSharpEditAndContinueTestHelpers.CreateInstance().VerifyLineEdits( + new CSharpEditAndContinueTestHelpers().VerifyLineEdits( editScript, expectedLineEdits, expectedNodeUpdates, @@ -65,7 +65,17 @@ internal static void VerifySemanticDiagnostics( { VerifySemantics( new[] { editScript }, - expectedDiagnostics: expectedDiagnostics); + new[] { new DocumentAnalysisResultsDescription(diagnostics: expectedDiagnostics) }); + } + + internal static void VerifySemanticDiagnostics( + this EditScript editScript, + ActiveStatementsDescription activeStatements, + params RudeEditDiagnosticDescription[] expectedDiagnostics) + { + VerifySemantics( + new[] { editScript }, + new[] { new DocumentAnalysisResultsDescription(activeStatements: activeStatements, diagnostics: expectedDiagnostics) }); } internal static void VerifySemanticDiagnostics( @@ -75,8 +85,8 @@ internal static void VerifySemanticDiagnostics( { VerifySemantics( new[] { editScript }, - targetFrameworks: targetFrameworks, - expectedDiagnostics: expectedDiagnostics); + new[] { new DocumentAnalysisResultsDescription(diagnostics: expectedDiagnostics) }, + targetFrameworks: targetFrameworks); } internal static void VerifySemantics( @@ -86,25 +96,24 @@ internal static void VerifySemantics( { VerifySemantics( new[] { editScript }, - activeStatements, - expectedSemanticEdits: expectedSemanticEdits, - expectedDiagnostics: null); + new[] { new DocumentAnalysisResultsDescription(activeStatements, semanticEdits: expectedSemanticEdits) }); + } + + internal static void VerifySemantics( + this EditScript editScript, + params SemanticEditDescription[] expectedSemanticEdits) + { + VerifySemantics(editScript, ActiveStatementsDescription.Empty, expectedSemanticEdits); } internal static void VerifySemantics( - this EditScript[] editScripts, - ActiveStatementsDescription? activeStatements = null, - TargetFramework[]? targetFrameworks = null, - SemanticEditDescription[]? expectedSemanticEdits = null, - RudeEditDiagnosticDescription[]? expectedDiagnostics = null) + EditScript[] editScripts, + DocumentAnalysisResultsDescription[] expected, + TargetFramework[]? targetFrameworks = null) { foreach (var targetFramework in targetFrameworks ?? new[] { TargetFramework.NetStandard20, TargetFramework.NetCoreApp }) { - new CSharpEditAndContinueTestHelpers(targetFramework).VerifySemantics( - editScripts, - activeStatements, - expectedSemanticEdits, - expectedDiagnostics); + new CSharpEditAndContinueTestHelpers().VerifySemantics(editScripts, targetFramework, expected); } } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs index 5bb9bdb679f3d..329d1b71c8b5f 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs @@ -2,16 +2,14 @@ // 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 - using System; using System.Collections.Generic; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -39,17 +37,23 @@ internal enum MethodKind internal static RudeEditDiagnosticDescription Diagnostic(RudeEditKind rudeEditKind, string squiggle, params string[] arguments) => new(rudeEditKind, squiggle, arguments, firstLine: null); - internal static SemanticEditDescription SemanticEdit(SemanticEditKind kind, Func symbolProvider, IEnumerable> syntaxMap) - { - Assert.NotNull(syntaxMap); - return new SemanticEditDescription(kind, symbolProvider, syntaxMap, preserveLocalVariables: true); - } + internal static SemanticEditDescription SemanticEdit(SemanticEditKind kind, Func symbolProvider, IEnumerable>? syntaxMap, string? partialType = null) + => new(kind, symbolProvider, (partialType != null) ? c => c.GetMember(partialType) : null, syntaxMap, hasSyntaxMap: syntaxMap != null); + + internal static SemanticEditDescription SemanticEdit(SemanticEditKind kind, Func symbolProvider, string? partialType = null, bool preserveLocalVariables = false) + => new(kind, symbolProvider, (partialType != null) ? c => c.GetMember(partialType) : null, syntaxMap: null, preserveLocalVariables); + + internal static string DeletedSymbolDisplay(string kind, string displayName) + => string.Format(FeaturesResources.member_kind_and_name, kind, displayName); - internal static SemanticEditDescription SemanticEdit(SemanticEditKind kind, Func symbolProvider, bool preserveLocalVariables = false) - => new(kind, symbolProvider, syntaxMap: null, preserveLocalVariables); + internal static DocumentAnalysisResultsDescription DocumentResults( + ActiveStatementsDescription? activeStatements = null, + SemanticEditDescription[]? semanticEdits = null, + RudeEditDiagnosticDescription[]? diagnostics = null) + => new(activeStatements, semanticEdits, diagnostics); private static SyntaxTree ParseSource(string source) - => CSharpEditAndContinueTestHelpers.CreateInstance().ParseText(ActiveStatementsDescription.ClearTags(source)); + => new CSharpEditAndContinueTestHelpers().ParseText(ActiveStatementsDescription.ClearTags(source)); internal static EditScript GetTopEdits(string src1, string src2) { @@ -63,6 +67,14 @@ internal static EditScript GetTopEdits(string src1, string src2) return match.GetTreeEdits(); } + public static EditScript GetTopEdits(EditScript methodEdits) + { + var oldMethodSource = methodEdits.Match.OldRoot.ToFullString(); + var newMethodSource = methodEdits.Match.NewRoot.ToFullString(); + + return GetTopEdits(WrapMethodBodyWithClass(oldMethodSource), WrapMethodBodyWithClass(newMethodSource)); + } + /// /// Gets method edits on the current level of the source hierarchy. This means that edits on lower labeled levels of the hierarchy are not expected to be returned. /// @@ -103,6 +115,8 @@ public static MatchingPairs ToMatchingPairs(Match match) public static MatchingPairs ToMatchingPairs(IEnumerable> matches) => EditAndContinueTestHelpers.ToMatchingPairs(matches); +#nullable disable + internal static BlockSyntax MakeMethodBody( string bodySource, MethodKind kind = MethodKind.Regular) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs index 57e9be220784e..9e9d146525be9 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs @@ -9,10 +9,12 @@ using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class LineEditTests : EditingTestBase { #region Methods @@ -1423,5 +1425,44 @@ class C } #endregion + + #region Types + + [Fact] + public void Type_Reorder1() + { + var src1 = @" +class C +{ + static int F1() => 1; + static int F2() => 1; +} + +class D +{ + static int G1() => 1; + static int G2() => 1; +} +"; + var src2 = @" +class D +{ + static int G1() => 1; + static int G2() => 1; +} + +class C +{ + static int F1() => 1; + static int F2() => 1; +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifyLineEdits( + new[] { new SourceLineUpdate(3, 9), new SourceLineUpdate(4, 10), new SourceLineUpdate(9, 3), new SourceLineUpdate(10, 4) }, + Array.Empty()); + } + + #endregion } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 45411ddddb79c..e863592fd1c4e 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -17,6 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class StatementEditingTests : EditingTestBase { #region Strings @@ -6813,7 +6814,8 @@ public void LocalFunction_In_Parameter_InsertParameter() edits.VerifyEdits( "Update [void M() { void local() { throw null; } }]@13 -> [void M() { void local(in int b) { throw null; } }]@13"); - edits.VerifyRudeDiagnostics(); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingLambdaParameters, "local", CSharpFeaturesResources.local_function)); } [Fact] @@ -6827,7 +6829,8 @@ public void LocalFunction_In_Parameter_Update() edits.VerifyEdits( "Update [void M() { void local(int b) { throw null; } }]@13 -> [void M() { void local(in int b) { throw null; } }]@13"); - edits.VerifyRudeDiagnostics(); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingLambdaParameters, "local", CSharpFeaturesResources.local_function)); } [Fact] @@ -7000,9 +7003,8 @@ public void LocalFunction_AddAttribute() "Insert [A]@3"); // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); } [Fact] @@ -7018,9 +7020,8 @@ public void LocalFunction_RemoveAttribute() "Delete [A]@3"); // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); } [Fact] @@ -7034,9 +7035,7 @@ public void LocalFunction_ReorderAttribute() edits.VerifyEdits("Reorder [B]@6 -> @3"); // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7054,9 +7053,7 @@ public void LocalFunction_CombineAttributeLists() "Delete [B]@6"); // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics( + GetTopEdits(edits).VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute), Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); } @@ -7075,10 +7072,8 @@ public void LocalFunction_SplitAttributeLists() "Insert [B]@6", "Delete [B]@6"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "[B]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[B]", FeaturesResources.attribute)); } [Fact] @@ -7091,10 +7086,8 @@ public void LocalFunction_ChangeAttributeListTarget1() edits.VerifyEdits("Update [[return: A]]@2 -> [[A]]@2"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Update, "[A]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Update, "[A]", FeaturesResources.attribute)); } [Fact] @@ -7107,10 +7100,8 @@ public void LocalFunction_ChangeAttributeListTarget2() edits.VerifyEdits("Update [[A]]@2 -> [[return: A]]@2"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Update, "return:", CSharpFeaturesResources.attribute_target)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Update, "return:", CSharpFeaturesResources.attribute_target)); } [Fact] @@ -7125,10 +7116,8 @@ public void LocalFunction_ReturnType_AddAttribute() "Insert [[return: A]]@2", "Insert [A]@11"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "[return: A]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[return: A]", FeaturesResources.attribute)); } [Fact] @@ -7143,10 +7132,8 @@ public void LocalFunction_ReturnType_RemoveAttribute() "Delete [[return: A]]@2", "Delete [A]@11"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); } [Fact] @@ -7159,10 +7146,7 @@ public void LocalFunction_ReturnType_ReorderAttribute() edits.VerifyEdits("Reorder [B]@14 -> @11"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7177,10 +7161,8 @@ public void LocalFunction_Parameter_AddAttribute() "Insert [[A]]@9", "Insert [A]@10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); } [Fact] @@ -7195,10 +7177,8 @@ public void LocalFunction_Parameter_RemoveAttribute() "Delete [[A]]@9", "Delete [A]@10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "int i", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "int i", FeaturesResources.attribute)); } [Fact] @@ -7211,10 +7191,7 @@ public void LocalFunction_Parameter_ReorderAttribute() edits.VerifyEdits("Reorder [B]@13 -> @10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7229,10 +7206,8 @@ public void LocalFunction_TypeParameter_AddAttribute() "Insert [[A]]@9", "Insert [A]@10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); } [Fact] @@ -7247,10 +7222,8 @@ public void LocalFunction_TypeParameter_RemoveAttribute() "Delete [[A]]@9", "Delete [A]@10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "T", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "T", FeaturesResources.attribute)); } [Fact] @@ -7263,10 +7236,7 @@ public void LocalFunction_TypeParameter_ReorderAttribute() edits.VerifyEdits("Reorder [B]@13 -> @10"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7280,10 +7250,8 @@ public void LocalFunctions_TypeParameter_Insert1() "Insert []@8", "Insert [A]@9"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "", FeaturesResources.type_parameter)); } [Fact] @@ -7297,10 +7265,8 @@ public void LocalFunctions_TypeParameter_Insert2() "Update []@8 -> []@8", "Insert [B]@11"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.type_parameter)); } [Fact] @@ -7314,10 +7280,8 @@ public void LocalFunctions_TypeParameter_Delete1() "Delete []@8", "Delete [A]@9"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); } [Fact] @@ -7331,10 +7295,8 @@ public void LocalFunctions_TypeParameter_Delete2() "Update []@8 -> []@8", "Delete [A]@9"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.type_parameter)); } [Fact] @@ -7347,10 +7309,8 @@ public void LocalFunctions_TypeParameter_Update() edits.VerifyEdits( "Update [A]@9 -> [B]@9"); - // Get top edits so we can validate rude edits - edits = GetTopEdits(WrapMethodBodyWithClass(src1), WrapMethodBodyWithClass(src2)); - - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Renamed, "B", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "B", FeaturesResources.type_parameter)); } [Fact] @@ -7363,7 +7323,8 @@ public void LocalFunctions_TypeParameter_Reorder() edits.VerifyEdits( "Reorder [B]@11 -> @9"); - edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter)); + GetTopEdits(edits).VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter)); } [Fact] @@ -7377,10 +7338,11 @@ public void LocalFunctions_TypeParameter_ReorderAndUpdate() "Reorder [B]@11 -> @9", "Update [A]@9 -> [C]@11"); - edits.VerifyRudeDiagnostics( + GetTopEdits(edits).VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter), Diagnostic(RudeEditKind.Renamed, "C", FeaturesResources.type_parameter)); } + #endregion #region Queries @@ -8908,13 +8870,9 @@ static IEnumerable F() "; var edits = GetTopEdits(src1, src2); - CSharpEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( - new[] { edits }, - ActiveStatementsDescription.Empty, - expectedDiagnostics: new[] - { - Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "static IEnumerable F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute") - }); + edits.VerifySemanticDiagnostics( + targetFrameworks: new[] { TargetFramework.Mscorlib40AndSystemCore }, + Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "static IEnumerable F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")); } [Fact] @@ -8944,7 +8902,8 @@ static IEnumerable F() "; var edits = GetTopEdits(src1, src2); - CSharpEditAndContinueTestHelpers.CreateInstance40().VerifySemantics(new[] { edits }); + edits.VerifySemanticDiagnostics( + targetFrameworks: new[] { TargetFramework.Mscorlib40AndSystemCore }); } #endregion diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/SyntaxComparerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/SyntaxComparerTests.cs index ee0f7ee51e5c2..95f06a78ca88c 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/SyntaxComparerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/SyntaxComparerTests.cs @@ -7,11 +7,13 @@ using System; using System.Collections.Immutable; using Microsoft.CodeAnalysis.Differencing; +using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class SyntaxComparerTests { private static SyntaxNode MakeLiteral(int n) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 0f56eb6ba6fba..80ab9bb7580e6 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -4,8 +4,8 @@ #nullable disable +using System; using System.Linq; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.UnitTests; using Microsoft.CodeAnalysis.EditAndContinue; @@ -17,6 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { + [UseExportProvider] public class TopLevelEditingTests : EditingTestBase { #region Usings @@ -39,20 +40,21 @@ public void UsingDelete1() public void UsingDelete2() { var src1 = @" -using System.Diagnostics; +using D = System.Diagnostics; using System.Collections; using System.Collections.Generic; "; var src2 = @" -using System.Diagnostics; using System.Collections.Generic; "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [using System.Collections;]@29"); + "Delete [using D = System.Diagnostics;]@2", + "Delete [using System.Collections;]@33"); edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.using_directive), Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.using_directive)); } @@ -60,20 +62,21 @@ public void UsingDelete2() public void UsingInsert() { var src1 = @" -using System.Diagnostics; using System.Collections.Generic; "; var src2 = @" -using System.Diagnostics; +using D = System.Diagnostics; using System.Collections; using System.Collections.Generic; "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [using System.Collections;]@29"); + "Insert [using D = System.Diagnostics;]@2", + "Insert [using System.Collections;]@33"); edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "using D = System.Diagnostics;", CSharpFeaturesResources.using_directive), Diagnostic(RudeEditKind.Insert, "using System.Collections;", CSharpFeaturesResources.using_directive)); } @@ -217,6 +220,55 @@ namespace N #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.VerifyRudeDiagnostics( + 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.VerifyRudeDiagnostics( + 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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.extern_alias)); + } + + #endregion + #region Attributes [Fact] @@ -735,6 +787,7 @@ public void InterfaceInsert() public interface I { void F(); + static void G() {} }"; var edits = GetTopEdits(src1, src2); @@ -867,7 +920,7 @@ public class SubClass : BaseClass, IConflict [WorkItem(37128, "https://github.com/dotnet/roslyn/issues/37128")] [Fact] - public void Interface_AddMembersWithImplementation() + public void Interface_InsertMembers() { var src1 = @" using System; @@ -915,42 +968,407 @@ 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); - edits.VerifyRudeDiagnostics( + // 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.auto_property), + Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticProperty2", FeaturesResources.property_), Diagnostic(RudeEditKind.InsertVirtual, "virtual int VirtualProperty1", FeaturesResources.auto_property), Diagnostic(RudeEditKind.InsertVirtual, "virtual int VirtualProperty2", FeaturesResources.auto_property), Diagnostic(RudeEditKind.InsertVirtual, "int VirtualProperty3", FeaturesResources.auto_property), Diagnostic(RudeEditKind.InsertVirtual, "int VirtualProperty4", FeaturesResources.auto_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.InsertVirtual, "event Action VirtualEvent", FeaturesResources.event_), - Diagnostic(RudeEditKind.InsertVirtual, "abstract event Action AbstractEvent", CSharpFeaturesResources.event_field), - // TODO: The following errors are reported due to https://github.com/dotnet/roslyn/issues/37128. - Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticField = 10", FeaturesResources.field), - Diagnostic(RudeEditKind.InsertIntoInterface, "static void StaticMethod()", FeaturesResources.method), - Diagnostic(RudeEditKind.InsertIntoInterface, "sealed void NonVirtualMethod()", FeaturesResources.method), - Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticProperty1", FeaturesResources.auto_property), - Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticProperty2", FeaturesResources.property_), - Diagnostic(RudeEditKind.InsertIntoInterface, "sealed int NonVirtualProperty", FeaturesResources.property_), 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 StaticEvent", CSharpFeaturesResources.event_field), Diagnostic(RudeEditKind.InsertIntoInterface, "static event Action StaticEvent2", FeaturesResources.event_), - Diagnostic(RudeEditKind.InsertIntoInterface, "sealed event Action NonVirtualEvent", 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I").GetMember("M")) + }), + }); + } + + [Fact] + public void GenericType_InsertMembers() + { + var src1 = @" +using System; +class C +{ +} +"; + var src2 = @" +using System; +class C +{ + void M() {} + int P1 { get; set; } + int P2 { get => 1; set {} } + int this[int i] { get => 1; set {} } + event Action E { add {} remove {} } + event Action EF; + int F1, F2; + + enum E {} + interface I {} + class D {} +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoGenericType, "void M()", FeaturesResources.method), + Diagnostic(RudeEditKind.InsertIntoGenericType, "int P1", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertIntoGenericType, "int P2", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertIntoGenericType, "int this[int i]", FeaturesResources.indexer_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "event Action E", FeaturesResources.event_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "EF", CSharpFeaturesResources.event_field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F1", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F2", FeaturesResources.field)); + } + + [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(CSharpFeaturesResources.struct_, "S")), + Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.interface_, "I"))); + } + + [Fact] + public void PartialType_Delete() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.method, "C.F()")) }), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("M")), + }) + }); + } + + [Fact] + public void PartialType_InsertFirstDeclaration() + { + var src1 = ""; + var src2 = "partial class C { void F() {} }"; + + GetTopEdits(src1, src2).VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"), preserveLocalVariables: false) }); + } + + [Fact] + public void PartialType_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("G"), preserveLocalVariables: false) + }), + }); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S").GetMember("F")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I").GetMember("F")), + }) + }); + } + + [Fact] + public void GenericType_DeleteInsert() + { + var srcA1 = @" +class C { void F() {} } +struct S { void F() {} } +interface I { void F() {} } +"; + var srcB1 = ""; + + var srcA2 = srcB1; + var srcB2 = srcA1; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "void F()", FeaturesResources.method), + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "void F()", FeaturesResources.method), + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "void F()", FeaturesResources.method), + }) + }); + } + + [Fact] + public void Type_DeleteInsert_NonInsertableMembers() + { + 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; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("VirtualMethod")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("ToString")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("I.G")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I").GetMember("F")), + }) + }); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true), + }), + + DocumentResults() + }); } #endregion @@ -1276,7 +1694,7 @@ public void EnumMemberDelete() "Delete [Blue]@18"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "enum Color", FeaturesResources.enum_value)); + Diagnostic(RudeEditKind.Delete, "enum Color", DeletedSymbolDisplay(FeaturesResources.enum_value, "Blue"))); } [Fact] @@ -1290,7 +1708,7 @@ public void EnumMemberDelete2() edits.VerifyEdits("Delete [Blue]@18"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "enum Color", FeaturesResources.enum_value)); + Diagnostic(RudeEditKind.Delete, "enum Color", DeletedSymbolDisplay(FeaturesResources.enum_value, "Blue"))); } [WorkItem(754916, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916"), WorkItem(793197, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/793197")] @@ -1433,8 +1851,8 @@ public void Delegates_Delete() "Delete [private delegate void D();]@10", "Delete [()]@33"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.delegate_)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.delegate_, "D"))); } [Fact] @@ -1542,6 +1960,21 @@ public void Delegates_Parameter_Update() Diagnostic(RudeEditKind.TypeUpdate, "byte a", FeaturesResources.parameter)); } + [Fact] + public void Delegates_ParameterOptionalParameter_Update() + { + var src1 = "public delegate int D(int a = 1);"; + var src2 = "public delegate int D(int a = 2);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a = 1]@22 -> [int a = 2]@22"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "int a = 2", FeaturesResources.parameter)); + } + [Fact] public void Delegates_Parameter_UpdateModifier() { @@ -1934,7 +2367,7 @@ abstract class D { public extern D(); - public static extern int P { [DllImport(""msvcrt.dll"")]get; } + public static extern int P { [DllImport(""msvcrt.dll"")]get; [DllImport(""msvcrt.dll"")]set; } [DllImport(""msvcrt.dll"")] public static extern int puts(string c); @@ -1950,7 +2383,7 @@ abstract class D var edits = GetTopEdits(src1, src2); // Adding P/Invoke is not supported by the CLR. - edits.VerifyRudeDiagnostics( + 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), @@ -2023,8 +2456,8 @@ public void NestedClass_MethodDeleteInsert() "Delete [public void goo() {}]@17", "Delete [()]@32"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public class C", FeaturesResources.method)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "public class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()"))); } [Fact] @@ -2043,152 +2476,1086 @@ public void NestedClass_ClassDeleteInsert() Diagnostic(RudeEditKind.Move, "public class X", FeaturesResources.class_)); } - #endregion - - #region Namespaces + /// + /// A new generic type can be added whether it's nested and inherits generic parameters from the containing type, or top-level. + /// + [Fact] + public void NestedClassGeneric_Insert() + { + var src1 = @" +using System; +class C +{ +} +"; + var src2 = @" +using System; +class C +{ + class D {} + struct S {} + enum N {} + interface I {} + delegate void D(); +} + +class D +{ + +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics(); + } + + [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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "B = 2", FeaturesResources.enum_value)); + } + + [Fact, WorkItem(50876, "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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults() + }); + } + + [Fact, WorkItem(50876, "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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.InitializerUpdate, "A = 2", FeaturesResources.enum_value), + }), + + DocumentResults() + }); + } + + [Fact, WorkItem(50876, "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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "enum N", FeaturesResources.enum_), + }), + + DocumentResults() + }); + } + + [Fact, WorkItem(50876, "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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] { 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + 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_InsertDeleteAndChangeSignature() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.Insert, "int x", FeaturesResources.parameter) + }), + + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", FeaturesResources.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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial struct S", DeletedSymbolDisplay(FeaturesResources.method, "F2(byte)")) }), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("S").GetMember("C").GetMember("F2")) }) + }); + } + + [Fact] + public void NestedPartialTypeInPartialType_InsertDeleteAndChange_BaseType() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "partial class C", FeaturesResources.class_) }), + + DocumentResults(), + }); + } + + [Fact] + public void NestedPartialTypeInPartialType_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + + DocumentResults(), + }); + } + + [Fact] + public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameter() + { + var srcA1 = "partial class C { }"; + var srcB1 = ""; + var srcC1 = "partial class C { }"; + + var srcA2 = ""; + var srcB2 = "partial class C<[A]T> { }"; + var srcC2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C<[A]T>", FeaturesResources.class_) }), + + DocumentResults(), + }); + } + + [Fact] + public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Constraint() + { + var srcA1 = "partial class C { }"; + var srcB1 = ""; + var srcC1 = "partial class C { }"; + + var srcA2 = ""; + var srcB2 = "partial class C where T : new() { }"; + var srcC2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + + DocumentResults(), + }); + } + + /// + /// Moves partial classes to different files while moving around their attributes and base interfaces. + /// + [Fact] + public void NestedPartialTypeInPartialType_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() { } }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2) }, + new[] + { + DocumentResults(), + DocumentResults(), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F")) }), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("G")) }), + }); + } + + /// + /// Moves partial classes to different files while moving around their attributes and base interfaces. + /// Currently we do not support splitting attribute lists. + /// + [Fact] + public void NestedPartialTypeInPartialType_InsertDeleteRefactor_AttributeListSplitting() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.Update, "partial class C", FeaturesResources.class_) }), + }); + } + + [Fact] + public void NestedPartialTypeInPartialType_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", FeaturesResources.parameter) }), + DocumentResults(diagnostics: new[] { 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2), GetTopEdits(srcE1, srcE2), GetTopEdits(srcF1, srcF2) }, + new[] + { + // A + DocumentResults(), + + // B + DocumentResults(), + + // C + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I").GetMember("C").GetMember("F1")) }), + + // D + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.InsertVirtual, "virtual void N1()", FeaturesResources.method) }), + + // E + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I").GetMember("C").GetMember("F2")) }), + + // F + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.InsertVirtual, "virtual void N2()", FeaturesResources.method) }), + }); + } + + #endregion + + #region Namespaces + + [Fact] + public void Namespace_Insert() + { + var src1 = @""; + var src2 = @"namespace C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [namespace C { }]@0"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "namespace C", FeaturesResources.namespace_)); + + } + [Fact] + public void Namespace_InsertNested() + { + var src1 = @"namespace C { }"; + var src2 = @"namespace C { namespace D { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [namespace D { }]@14"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "namespace D", FeaturesResources.namespace_)); + } + + [Fact] + public void NamespaceDelete() + { + var src1 = @"namespace C { namespace D { } }"; + var src2 = @"namespace C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [namespace D { }]@14"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "namespace C", FeaturesResources.namespace_)); + } + + [Fact] + public void NamespaceMove1() + { + 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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Move, "namespace D", FeaturesResources.namespace_)); + } + + [Fact] + public void NamespaceReorder1() + { + 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.VerifyRudeDiagnostics(); + } + + [Fact] + public void NamespaceReorder2() + { + 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.VerifyRudeDiagnostics(); + } + + #endregion + + #region Members + + [Fact] + public void MemberUpdate_Modifier_ReadOnly_Remove() + { + var src1 = @" +using System; + +struct S +{ + // methods + public readonly int M() => 1; + + // properties + public readonly int P => 1; + public readonly int Q { get; } + public int R { readonly get; readonly set; } + + // events + public readonly event Action E { add {} remove {} } + public event Action F { readonly add {} readonly remove {} } +}"; + var src2 = @" +using System; +struct S +{ + // methods + public int M() => 1; + + // properties + public int P => 1; + public int Q { get; } + public int R { get; set; } + + // events + public event Action E { add {} remove {} } + public event Action F { add {} remove {} } +}"; + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "public int M()", FeaturesResources.method), + Diagnostic(RudeEditKind.ModifiersUpdate, "public int P", FeaturesResources.property_), + Diagnostic(RudeEditKind.ModifiersUpdate, "public int Q", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.property_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "set", CSharpFeaturesResources.property_setter), + Diagnostic(RudeEditKind.ModifiersUpdate, "add", FeaturesResources.event_accessor), + Diagnostic(RudeEditKind.ModifiersUpdate, "remove", FeaturesResources.event_accessor)); + } + + [Fact] + public void MemberUpdate_Modifier_ReadOnly_Add() + { + var src1 = @" +using System; + +struct S +{ + // methods + public int M() => 1; + + // properties + public int P => 1; + public int Q { get; } + public int R { get; set; } + + // events + public event Action E { add {} remove {} } + public event Action F { add {} remove {} } +}"; + var src2 = @" +using System; + +struct S +{ + // methods + public readonly int M() => 1; + + // properties + public readonly int P => 1; + public readonly int Q { get; } + public int R { readonly get; readonly set; } + + // events + public readonly event Action E { add {} remove {} } + public event Action F { readonly add {} readonly remove {} } +}"; + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int M()", FeaturesResources.method), + Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int P", FeaturesResources.property_), + Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int Q", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly get", CSharpFeaturesResources.property_getter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly set", CSharpFeaturesResources.property_setter), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly add", FeaturesResources.event_accessor), + Diagnostic(RudeEditKind.ModifiersUpdate, "readonly remove", FeaturesResources.event_accessor)); + } + + [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( + new[] { edits }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("M"), preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P2").GetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P2").SetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMembers("this[]").Cast().Single(m => m.GetParameters().Single().Type.Name == "Int32").GetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMembers("this[]").Cast().Single(m => m.GetParameters().Single().Type.Name == "Int32").SetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMembers("this[]").Cast().Single(m => m.GetParameters().Single().Type.Name == "Byte").GetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMembers("this[]").Cast().Single(m => m.GetParameters().Single().Type.Name == "Byte").SetMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("E").AddMethod, preserveLocalVariables: false), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("E").RemoveMethod, preserveLocalVariables: false), + }) + }); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F"), preserveLocalVariables: false) + }) + }); + } + + [Fact] + public void PartialMember_DeleteInsert_GenericMethod() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(diagnostics: new[] + { + // TODO: better message + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "void F()", FeaturesResources.method) + }) + }); + } + + [Fact] + public void PartialMember_DeleteInsert_GenericType() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(diagnostics: new[] + { + // TODO: better message + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "void F()", FeaturesResources.method) + }) + }); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class D", FeaturesResources.class_), + }), + + DocumentResults() + }); + } - [Fact] - public void NamespaceMove1() + [Fact, WorkItem(51011, "https://github.com/dotnet/roslyn/issues/51011")] + public void PartialMember_RenameInsertDelete() { - var src1 = @"namespace C { namespace D { } }"; - var src2 = @"namespace C { } namespace D { }"; + // The syntactic analysis for A and B produce rename edits since it doesn't see that the member was in fact moved. + // TODO: Currently, we don't even pass rename edits to semantic analysis where we could handle them as updates. - var edits = GetTopEdits(src1, src2); + 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() {} }"; - edits.VerifyEdits( - "Move [namespace D { }]@14 -> @16"); + // current outcome: + GetTopEdits(srcA1, srcA2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Renamed, "void F2()", FeaturesResources.method)); + GetTopEdits(srcB1, srcB2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Renamed, "void F1()", FeaturesResources.method)); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "namespace D", FeaturesResources.namespace_)); + // correct outcome: + //EditAndContinueValidation.VerifySemantics( + // new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + // new[] + // { + // DocumentResults(semanticEdits: new[] + // { + // SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F2")), + // }), + + // DocumentResults( + // semanticEdits: new[] + // { + // SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F1")), + // }) + // }); } [Fact] - public void NamespaceReorder1() + public void PartialMember_DeleteInsert_UpdateMethodBodyError() { - var src1 = @"namespace C { namespace D { } class T { } namespace E { } }"; - var src2 = @"namespace C { namespace E { } class T { } namespace D { } }"; + var srcA1 = @" +using System.Collections.Generic; - var edits = GetTopEdits(src1, src2); +partial class C +{ + IEnumerable F() { yield return 1; } +} +"; + var srcB1 = @" +using System.Collections.Generic; - edits.VerifyEdits( - "Reorder [class T { }]@30 -> @30", - "Reorder [namespace E { }]@42 -> @14"); +partial class C +{ +} +"; - edits.VerifyRudeDiagnostics(); + var srcA2 = @" +using System.Collections.Generic; + +partial class C +{ +} +"; + var srcB2 = @" +using System.Collections.Generic; + +partial class C +{ + IEnumerable F() { yield return 1; yield return 2; } +} +"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(diagnostics: new[] + { + Diagnostic(RudeEditKind.Insert, "yield return 2;", CSharpFeaturesResources.yield_return_statement) + }) + }); } [Fact] - public void NamespaceReorder2() + public void PartialMember_DeleteInsert_UpdatePropertyAccessors() { - 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); + var srcA1 = "partial class C { int P { get => 1; set { Console.WriteLine(1); } } }"; + var srcB1 = "partial class C { }"; - edits.VerifyEdits( - "Reorder [class T { }]@65 -> @65", - "Reorder [namespace E { }]@77 -> @14"); + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { int P { get => 2; set { Console.WriteLine(2); } } }"; - edits.VerifyRudeDiagnostics(); + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod) + }) + }); } - #endregion - - #region Members - [Fact] - public void MemberUpdate_Modifier_ReadOnly_Remove() + public void PartialMember_DeleteInsert_UpdateAutoProperty() { - var src1 = @" -using System; + var srcA1 = "partial class C { int P => 1; }"; + var srcB1 = "partial class C { }"; -struct S -{ - // methods - public readonly int M() => 1; + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { int P => 2; }"; - // properties - public readonly int P => 1; - public readonly int Q { get; } - public int R { readonly get; readonly set; } + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod) + }) + }); + } - // events - public readonly event Action E { add {} remove {} } - public event Action F { readonly add {} readonly remove {} } -}"; - var src2 = @" -using System; -struct S -{ - // methods - public int M() => 1; + [Fact] + public void PartialMember_DeleteInsert_AddFieldInitializer() + { + var srcA1 = "partial class C { int f; }"; + var srcB1 = "partial class C { }"; - // properties - public int P => 1; - public int Q { get; } - public int R { get; set; } + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { int f = 1; }"; - // events - public event Action E { add {} remove {} } - public event Action F { add {} remove {} } -}"; - var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "public int M()", FeaturesResources.method), - Diagnostic(RudeEditKind.ModifiersUpdate, "public int P", FeaturesResources.property_), - Diagnostic(RudeEditKind.ModifiersUpdate, "public int Q", FeaturesResources.auto_property), - Diagnostic(RudeEditKind.ModifiersUpdate, "get", CSharpFeaturesResources.property_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "set", CSharpFeaturesResources.property_setter), - Diagnostic(RudeEditKind.ModifiersUpdate, "add", FeaturesResources.event_accessor), - Diagnostic(RudeEditKind.ModifiersUpdate, "remove", FeaturesResources.event_accessor)); + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }) + }); } [Fact] - public void MemberUpdate_Modifier_ReadOnly_Add() + public void PartialMember_DeleteInsert_RemoveFieldInitializer() { - var src1 = @" -using System; - -struct S -{ - // methods - public int M() => 1; + var srcA1 = "partial class C { int f = 1; }"; + var srcB1 = "partial class C { }"; - // properties - public int P => 1; - public int Q { get; } - public int R { get; set; } + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { int f; }"; - // events - public event Action E { add {} remove {} } - public event Action F { add {} remove {} } -}"; - var src2 = @" -using System; + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }) + }); + } -struct S -{ - // methods - public readonly int M() => 1; + [Fact] + public void PartialMember_DeleteInsert_ConstructorWithInitializers() + { + var srcA1 = "partial class C { int f = 1; C(int x) { f = x; } }"; + var srcB1 = "partial class C { }"; - // properties - public readonly int P => 1; - public readonly int Q { get; } - public int R { readonly get; readonly set; } + var srcA2 = "partial class C { int f = 1; }"; + var srcB2 = "partial class C { C(int x) { f = x + 1; } }"; - // events - public readonly event Action E { add {} remove {} } - public event Action F { readonly add {} readonly remove {} } -}"; - var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int M()", FeaturesResources.method), - Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int P", FeaturesResources.property_), - Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly int Q", FeaturesResources.auto_property), - Diagnostic(RudeEditKind.ModifiersUpdate, "readonly get", CSharpFeaturesResources.property_getter), - Diagnostic(RudeEditKind.ModifiersUpdate, "readonly set", CSharpFeaturesResources.property_setter), - Diagnostic(RudeEditKind.ModifiersUpdate, "readonly add", FeaturesResources.event_accessor), - Diagnostic(RudeEditKind.ModifiersUpdate, "readonly remove", FeaturesResources.event_accessor)); + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }) + }); } #endregion @@ -2380,20 +3747,11 @@ public void Method_Delete() class C { void goo() { } - - static void Main(string[] args) - { - Console.ReadLine(); - } } "; var src2 = @" class C { - static void Main(string[] args) - { - Console.ReadLine(); - } }"; var edits = GetTopEdits(src1, src2); @@ -2401,8 +3759,8 @@ static void Main(string[] args) "Delete [void goo() { }]@18", "Delete [()]@26"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()"))); } [Fact] @@ -2412,20 +3770,11 @@ public void MethodWithExpressionBody_Delete() class C { int goo() => 1; - - static void Main(string[] args) - { - Console.ReadLine(); - } } "; var src2 = @" class C { - static void Main(string[] args) - { - Console.ReadLine(); - } }"; var edits = GetTopEdits(src1, src2); @@ -2433,66 +3782,24 @@ static void Main(string[] args) "Delete [int goo() => 1;]@18", "Delete [()]@25"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); - } - - [Fact] - public void MethodDelete_WithParameters() - { - var src1 = @" -class C -{ - void goo(int a) { } - - static void Main(string[] args) - { - Console.ReadLine(); - } -} -"; - var src2 = @" -class C -{ - static void Main(string[] args) - { - Console.ReadLine(); - } -}"; - var edits = GetTopEdits(src1, src2); - - edits.VerifyEdits( - "Delete [void goo(int a) { }]@18", - "Delete [(int a)]@26", - "Delete [int a]@27"); - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()"))); } [WorkItem(754853, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")] [Fact] - public void MethodDelete_WithAttribute() + public void MethodDelete_WithParameterAndAttribute() { var src1 = @" class C { [Obsolete] void goo(int a) { } - - static void Main(string[] args) - { - Console.ReadLine(); - } } "; var src2 = @" class C { - static void Main(string[] args) - { - Console.ReadLine(); - } }"; var edits = GetTopEdits(src1, src2); @@ -2504,8 +3811,8 @@ void goo(int a) { }]@18", "Delete [(int a)]@42", "Delete [int a]@43"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(int)"))); } [WorkItem(754853, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")] @@ -2520,11 +3827,6 @@ class C { [DllImport(""msvcrt.dll"")] public static extern int puts(string c); - - static void Main(string[] args) - { - Console.ReadLine(); - } } "; var src2 = @" @@ -2533,10 +3835,6 @@ static void Main(string[] args) class C { - static void Main(string[] args) - { - Console.ReadLine(); - } }"; var edits = GetTopEdits(src1, src2); @@ -2549,8 +3847,8 @@ static void Main(string[] args) "Delete [(string c)]@134", "Delete [string c]@135"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.method)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "puts(string)"))); } [Fact] @@ -2675,7 +3973,7 @@ public virtual void F() {} "; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertVirtual, "public virtual void F()", FeaturesResources.method)); } @@ -2694,7 +3992,7 @@ abstract class C "; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertVirtual, "public abstract void F()", FeaturesResources.method)); } @@ -2713,13 +4011,13 @@ public override void F() { } "; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertVirtual, "public override void F()", FeaturesResources.method)); } [WorkItem(755784, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784"), WorkItem(835827, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")] [Fact] - public void PrivateMethodInsert_PInvoke1() + public void ExternMethodInsert() { var src1 = @" using System; @@ -2727,10 +4025,6 @@ public void PrivateMethodInsert_PInvoke1() class C { - static void Main(string[] args) - { - Console.ReadLine(); - } }"; var src2 = @" using System; @@ -2740,11 +4034,6 @@ class C { [DllImport(""msvcrt.dll"")] private static extern int puts(string c); - - static void Main(string[] args) - { - Console.ReadLine(); - } } "; var edits = GetTopEdits(src1, src2); @@ -2758,10 +4047,51 @@ static void Main(string[] args) "Insert [string c]@136"); // CLR doesn't support methods without a body - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertExtern, "private static extern int puts(string c)", FeaturesResources.method)); } + [WorkItem(755784, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784"), WorkItem(835827, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")] + [Fact] + public void ExternMethodDeleteInsert() + { + 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); +} +"; + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults() + }); + } + [Fact] public void MethodReorder1() { @@ -3983,10 +5313,162 @@ public class SubClass : BaseClass, IConflict "Insert [string IConflict.Get() => String.Empty;]@325", "Insert [()]@345"); - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier, "string IConflict.Get()", FeaturesResources.method)); } + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults(), + }); + } + + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F").PartialImplementationPart) }), + }); + } + + [Fact, WorkItem(51011, "https://github.com/dotnet/roslyn/issues/51011")] + public void PartialMethod_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(); }"; + + // current: + GetTopEdits(srcA1, srcA2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.MethodBodyAdd, "partial void F()", FeaturesResources.method)); + GetTopEdits(srcB1, srcB2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.MethodBodyDelete, "partial void F()", FeaturesResources.method)); + + // correct: TODO + //EditAndContinueValidation.VerifySemantics( + // new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + // new[] + // { + // DocumentResults(), + // DocumentResults( + // semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F")) }), + // }); + } + + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial class C", DeletedSymbolDisplay(FeaturesResources.method, "F()")) }) + }); + } + + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial class C", DeletedSymbolDisplay(FeaturesResources.method, "F()")) }) + }); + } + + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2) }, + new[] + { + DocumentResults(), + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F").PartialImplementationPart) }) + }); + } + + [Fact] + public void PartialMethod_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("F").PartialImplementationPart) }), + }); + } + #endregion #region Operators @@ -4014,7 +5496,7 @@ public static implicit operator bool (C c) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( + 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_)); } @@ -4042,9 +5524,46 @@ class C }"; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", CSharpFeaturesResources.conversion_operator), - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.operator_)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(CSharpFeaturesResources.conversion_operator, "implicit operator bool(C)")), + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.operator_, "operator +(C, C)"))); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("op_Addition")) + }), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("op_Implicit")) + }), + }); } [Fact] @@ -4204,7 +5723,7 @@ public void Operator_ReadOnlyRef_Parameter_InsertWhole() "Insert [(in Test b)]@42", "Insert [in Test b]@43"); - edits.VerifyRudeDiagnostics( + edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertOperator, "public static bool operator !(in Test b)", FeaturesResources.operator_)); } @@ -4321,21 +5840,11 @@ public void ConstructorUpdate_AddParameter() class C { public C(int a) { } - - static void Main(string[] args) - { - C c = new C(5); - } }"; var src2 = @" class C { public C(int a, int b) { } - - static void Main(string[] args) - { - C c = new C(5); - } }"; var edits = GetTopEdits(src1, src2); @@ -4350,108 +5859,35 @@ static void Main(string[] args) [Fact] public void DestructorDelete() { - var src1 = @" -class C -{ - static void Main(string[] args) - { - B b = new B(); - b = null; - GC.Collect(); - GC.WaitForPendingFinalizers(); - } -} -class B -{ - ~B() - { - Console.WriteLine(""B's destructor""); - } -}"; - var src2 = @" -class C -{ - static void Main(string[] args) - { - B b = new B(); - b = null; - GC.Collect(); - GC.WaitForPendingFinalizers(); - } -} -class B -{ + var src1 = @"class B { ~B() { } }"; + var src2 = @"class B { }"; -}"; - - var expectedEdit1 = @"Delete [~B() - { - Console.WriteLine(""B's destructor""); - }]@190"; + var expectedEdit1 = @"Delete [~B() { }]@10"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits(expectedEdit1); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class B", CSharpFeaturesResources.destructor)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class B", DeletedSymbolDisplay(CSharpFeaturesResources.destructor, "~B()"))); } [Fact] public void DestructorDelete_InsertConstructor() { - var src1 = @" -class C -{ - static void Main(string[] args) - { - B b = new B(); - b = null; - GC.Collect(); - GC.WaitForPendingFinalizers(); - } -} -class B -{ - ~B() - { - Console.WriteLine(""B's destructor""); - } -}"; - var src2 = @" -class C -{ - static void Main(string[] args) - { - B b = new B(); - b = null; - GC.Collect(); - GC.WaitForPendingFinalizers(); - } -} -class B -{ - B() - { - Console.WriteLine(""B's destructor""); - } -}"; - var expectedEdit1 = @"Insert [B() - { - Console.WriteLine(""B's destructor""); - }]@190"; - - var expectedEdit2 = @"Delete [~B() - { - Console.WriteLine(""B's destructor""); - }]@190"; + var src1 = @"class B { ~B() { } }"; + var src2 = @"class B { B() { } }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits(expectedEdit1, "Insert [()]@191", expectedEdit2); + edits.VerifyEdits( + "Insert [B() { }]@10", + "Insert [()]@11", + "Delete [~B() { }]@10"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class B", CSharpFeaturesResources.destructor)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingVisibility, "B()", FeaturesResources.constructor), + Diagnostic(RudeEditKind.Delete, "class B", DeletedSymbolDisplay(CSharpFeaturesResources.destructor, "~B()"))); } [Fact] @@ -4463,92 +5899,71 @@ public void ConstructorUpdate_AnonymousTypeInFieldInitializer() var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics(); - } - - [Fact] - public void StaticCtorDelete() - { - var src1 = "class C { static C() { } }"; - var src2 = "class C { }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); - } - - [Fact] - public void InstanceCtorDelete_Public() - { - var src1 = "class C { public C() { } }"; - var src2 = "class C { }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single()) }); - } - - [Fact] - public void InstanceCtorDelete_Private1() - { - var src1 = "class C { C() { } }"; - var src2 = "class C { }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + edits.VerifyRudeDiagnostics(); } [Fact] - public void InstanceCtorDelete_Private2() + public void StaticCtorDelete() { - var src1 = "class C { private C() { } }"; + var src1 = "class C { static C() { } }"; var src2 = "class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.static_constructor, "C()"))); } [Fact] - public void InstanceCtorDelete_Protected() + public void InstanceCtorDelete_Public() { - var src1 = "class C { protected C() { } }"; + var src1 = "class C { public C() { } }"; var src2 = "class C { }"; var edits = GetTopEdits(src1, src2); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }); } - [Fact] - public void InstanceCtorDelete_Internal() - { - var src1 = "class C { internal C() { } }"; + [Theory] + [InlineData("")] + [InlineData("private")] + [InlineData("protected")] + [InlineData("internal")] + [InlineData("private protected")] + [InlineData("protected internal")] + public void InstanceCtorDelete_NonPublic(string visibility) + { + var src1 = "class C { " + visibility + " C() { } }"; var src2 = "class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + Diagnostic(RudeEditKind.ChangingVisibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } [Fact] - public void InstanceCtorDelete_ProtectedInternal() + public void InstanceCtorDelete_Public_PartialWithInitializerUpdate() { - var src1 = "class C { protected internal C() { } }"; - var src2 = "class C { }"; + var srcA1 = "partial class C { public C() { } }"; + var srcB1 = "partial class C { int x = 1; }"; - var edits = GetTopEdits(src1, src2); + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { int x = 2; }"; - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true) }), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true) }) + }); } [Fact] @@ -4588,9 +6003,13 @@ public void InstanceCtorInsert_Partial_Public_Implicit() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + // no change in document A + DocumentResults(), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }), }); } @@ -4621,9 +6040,16 @@ public void InstanceCtorInsert_Partial_Public_NoImplicit() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").InstanceConstructors.Single(c => c.Parameters.IsEmpty)) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").InstanceConstructors.Single(c => c.Parameters.IsEmpty)) + }), + + // no change in document B + DocumentResults(), }); } @@ -4636,7 +6062,7 @@ public void InstanceCtorInsert_Private_Implicit1() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "private C()")); + Diagnostic(RudeEditKind.ChangingVisibility, "private C()", FeaturesResources.constructor)); } [Fact] @@ -4648,7 +6074,7 @@ public void InstanceCtorInsert_Private_Implicit2() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "C()")); + Diagnostic(RudeEditKind.ChangingVisibility, "C()", FeaturesResources.constructor)); } [Fact] @@ -4660,7 +6086,7 @@ public void InstanceCtorInsert_Protected_PublicImplicit() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "protected C()")); + Diagnostic(RudeEditKind.ChangingVisibility, "protected C()", FeaturesResources.constructor)); } [Fact] @@ -4672,7 +6098,7 @@ public void InstanceCtorInsert_Internal_PublicImplicit() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "internal C()")); + Diagnostic(RudeEditKind.ChangingVisibility, "internal C()", FeaturesResources.constructor)); } [Fact] @@ -4684,7 +6110,7 @@ public void InstanceCtorInsert_Internal_ProtectedImplicit() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "internal C()")); + Diagnostic(RudeEditKind.ChangingVisibility, "internal C()", FeaturesResources.constructor)); } [Fact] @@ -4764,11 +6190,16 @@ public void StaticCtor_Partial_DeleteInsert() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - targetFrameworks: null, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), 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(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), preserveLocalVariables: true) + }), }); } @@ -4783,10 +6214,16 @@ public void InstanceCtor_Partial_DeletePrivateInsertPrivate() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }), }); } @@ -4801,10 +6238,16 @@ public void InstanceCtor_Partial_DeletePublicInsertPublic() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }), }); } @@ -4819,10 +6262,14 @@ public void InstanceCtor_Partial_DeletePrivateInsertPublic() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedDiagnostics: new[] + new[] { - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "public C()") + // delete of the constructor in partial part will be reported as rude edit in the other document where it was inserted back with changed visibility + DocumentResults( + semanticEdits: NoSemanticEdits), + + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.ModifiersUpdate, "public C()", FeaturesResources.constructor) }), }); } @@ -4837,10 +6284,16 @@ public void StaticCtor_Partial_InsertDelete() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), preserveLocalVariables: true) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), 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(), }); } @@ -4855,10 +6308,16 @@ public void InstanceCtor_Partial_InsertPublicDeletePublic() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), }); } @@ -4873,10 +6332,16 @@ public void InstanceCtor_Partial_InsertPrivateDeletePrivate() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), }); } @@ -4891,9 +6356,16 @@ public void InstanceCtor_Partial_DeleteInternalInsertInternal() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), }); } @@ -4908,9 +6380,16 @@ public void InstanceCtor_Partial_InsertInternalDeleteInternal_WithBody() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - expectedSemanticEdits: new[] + new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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(), }); } @@ -4925,10 +6404,13 @@ public void InstanceCtor_Partial_InsertPublicDeletePrivate() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedDiagnostics: new[] + new[] { - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "public C()") + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.ModifiersUpdate, "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 visibility + DocumentResults(), }); } @@ -4943,10 +6425,12 @@ public void InstanceCtor_Partial_InsertInternalDeletePrivate() EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, - activeStatements: ActiveStatementsDescription.Empty, - expectedDiagnostics: new[] + new[] { - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "internal C()") + DocumentResults( + diagnostics: new[] { Diagnostic(RudeEditKind.ModifiersUpdate, "internal C()", FeaturesResources.constructor) }), + + DocumentResults(), }); } @@ -5105,7 +6589,7 @@ public C() new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), syntaxMap[0]) }); } - [Fact] + [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] public void InstanceCtor_Partial_Insert_Parameterless_LambdaInInitializer1() { var src1 = @" @@ -5144,11 +6628,16 @@ partial class C } "; var edits = GetTopEdits(src1, src2); - var syntaxMap = GetSyntaxMap(src1, src2); - edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), syntaxMap[0]) }); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "public C()")); + + // TODO: + //var syntaxMap = GetSyntaxMap(src1, src2); + + //edits.VerifySemantics( + // ActiveStatementsDescription.Empty, + // new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), syntaxMap[0]) }); } [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] @@ -5201,6 +6690,123 @@ partial class C // new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").Constructors.Single(), syntaxMap[0]) }); } + [Fact] + public void PartialTypes_ConstructorWithInitializerUpdates() + { + 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 = 1; + + 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 = 2; // 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + // No changes in document A + DocumentResults(), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Int32"), syntaxMap: syntaxMapB), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Boolean"), syntaxMap: syntaxMapB), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "UInt32"), syntaxMap: syntaxMapB), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Byte"), syntaxMap: null), + }) + }); + } + + [Fact] + public void PartialTypes_ConstructorWithInitializerUpdates_SemanticErrors() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + // 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: Array.Empty()) + }); + } + [WorkItem(2068, "https://github.com/dotnet/roslyn/issues/2068")] [Fact] public void Insert_ExternConstruct() @@ -5214,11 +6820,11 @@ public void Insert_ExternConstruct() "Insert [public extern C();]@10", "Insert [()]@25"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InsertExtern, "public extern C()", FeaturesResources.constructor)); + // The compiler generates an empty constructor. + edits.VerifySemanticDiagnostics(); } - [Fact(Skip = "https://github.com/dotnet/roslyn/pull/18940")] + [Fact] public void ParameterlessConstructor_SemanticError_Delete1() { var src1 = @" @@ -5234,19 +6840,9 @@ class C "; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics(); - } - - [Fact(Skip = "https://github.com/dotnet/roslyn/pull/18940")] - public void ParameterlessConstructor_SemanticError_Delete_OutsideOfClass1() - { - var src1 = @" -C() {} -"; - var src2 = @" -"; - var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics(); + // The compiler interprets D() as a constructor declaration. + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingVisibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } [Fact] @@ -5288,6 +6884,56 @@ partial void C(int x) }); } + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true) }), + + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F"), partialType: "C"), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F"), partialType: "C"), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true) + }), + + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true) }), + }); + } + [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] public void Constructor_BlockBodyToExpressionBody() { @@ -5625,7 +7271,7 @@ public void FieldInitializerUpdate_StaticCtorUpdate1() edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single()) }); + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), preserveLocalVariables: true) }); } [Fact] @@ -5638,7 +7284,7 @@ public void PropertyInitializerUpdate_StaticCtorUpdate1() edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single()) }); + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), preserveLocalVariables: true) }); } [Fact] @@ -5650,7 +7296,7 @@ public void FieldInitializerUpdate_InstanceCtorUpdate_Private() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + Diagnostic(RudeEditKind.ChangingVisibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } [Fact] @@ -5662,7 +7308,7 @@ public void PropertyInitializerUpdate_InstanceCtorUpdate_Private() var edits = GetTopEdits(src1, src2); edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.constructor)); + Diagnostic(RudeEditKind.ChangingVisibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))); } [Fact] @@ -5675,7 +7321,7 @@ public void FieldInitializerUpdate_InstanceCtorUpdate_Public() edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single()) }); + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }); } [Fact] @@ -5688,7 +7334,7 @@ public void PropertyInitializerUpdate_InstanceCtorUpdate_Public() edits.VerifySemantics( ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single()) }); + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }); } [Fact] @@ -5948,7 +7594,6 @@ public void FieldInitializerUpdate_StackAllocInConstructor() edits.VerifyEdits( "Update [a = 1]@21 -> [a = 2]@21"); - // TODO (tomat): diagnostic should point to the field initializer edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc", FeaturesResources.constructor)); } @@ -6128,8 +7773,12 @@ public void FieldInitializerUpdate_PartialTypeWithSingleDeclaration() edits.VerifyEdits( "Update [a = 1]@22 -> [a = 2]@22"); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "a = 2", FeaturesResources.field)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), preserveLocalVariables: true) + }); } [Fact] @@ -6140,8 +7789,12 @@ public void PropertyInitializerUpdate_PartialTypeWithSingleDeclaration() var edits = GetTopEdits(src1, src2); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "int a { get; } = 2;", FeaturesResources.auto_property)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), preserveLocalVariables: true) + }); } [Fact] @@ -6155,8 +7808,12 @@ public void FieldInitializerUpdate_PartialTypeWithMultipleDeclarations() edits.VerifyEdits( "Update [a = 1]@22 -> [a = 2]@22"); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "a = 2", FeaturesResources.field)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), preserveLocalVariables: true) + }); } [Fact] @@ -6167,8 +7824,12 @@ public void PropertyInitializerUpdate_PartialTypeWithMultipleDeclarations() var edits = GetTopEdits(src1, src2); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "int a { get; } = 2;", FeaturesResources.auto_property)); + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(), preserveLocalVariables: true) + }); } [Fact] @@ -6803,36 +8464,83 @@ public C(bool b) } } "; - var src2 = @" -using System; + var src2 = @" +using System; + +class B +{ + public B(int a) { } +} + +class C : B +{ + static int F(Func x) => 1; + + int A = F(a => a + 1); + int B = F(b => b + 1); + + public C(bool b) + : base(F(c => c + 2)) + { + F(d => d + 1); + } +} +"; + var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"), syntaxMap[0]) + }); + } + + [Fact] + public void FieldInitializerUpdate_Lambdas_PartialDeclarationDelete_SingleDocument() + { + var src1 = @" +partial class C +{ + int x = F(a => a + 1); +} + +partial class C +{ + int y = F(a => a + 10); +} + +partial class C +{ + public C() { } + static int F(Func x) => 1; +} +"; -class B + var src2 = @" +partial class C { - public B(int a) { } + int x = F(a => a + 1); } -class C : B +partial class C { - static int F(Func x) => 1; - - int A = F(a => a + 1); - int B = F(b => b + 1); + int y = F(a => a + 10); - public C(bool b) - : base(F(c => c + 2)) - { - F(d => d + 1); - } + static int F(Func x) => 1; } "; var edits = GetTopEdits(src1, src2); + var syntaxMap = GetSyntaxMap(src1, src2); edits.VerifySemantics( ActiveStatementsDescription.Empty, new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"), syntaxMap[0]) + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("F"), partialType: "C"), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), syntaxMap[0], partialType: "C"), }); } @@ -6844,7 +8552,7 @@ public void FieldInitializerUpdate_ActiveStatements1() class C { - int A = 1; + int A = 1; int B = 1; public C(int a) { Console.WriteLine(1); } @@ -6856,7 +8564,7 @@ class C class C { - int A = 1; + int A = 1; int B = 2; public C(int a) { Console.WriteLine(1); } @@ -6907,11 +8615,33 @@ public C() { } edits.VerifySemantics(ActiveStatementsDescription.Empty, expectedSemanticEdits: new[] { - SemanticEdit(SemanticEditKind.Update, c => ((IPropertySymbol)c.GetMember("C").GetMembers("P").Skip(1).First()).GetMethod), + SemanticEdit(SemanticEditKind.Update, c => ((IPropertySymbol)c.GetMember("C").GetMembers("P").First()).GetMethod), SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }); } + [Fact] + public void Field_Partial_DeleteInsert_InitializerRemoval() + { + 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }), + }); + } + #endregion #region Fields @@ -7234,7 +8964,7 @@ public C() } [Fact] - public void FieldInsert_ParameterlessConstructorInsert_WithInitializersAndLambdas1() + public void FieldInsert_ConstructorReplacingImplicitConstructor_WithInitializersAndLambdas() { var src1 = @" using System; @@ -7256,7 +8986,7 @@ class C int A = F(a => a + 1); int B = F(b => b + 1); // new field - public C() // new ctor + public C() // new ctor replacing existing implicit constructor { F(c => c + 1); } @@ -7274,6 +9004,52 @@ class C }); } + [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] + public void FieldInsert_ParameterlessConstructorInsert_WithInitializersAndLambdas() + { + var src1 = @" +using System; + +class C +{ + static int F(Func x) => 1; + + int A = F(a => a + 1); + + public C(int x) {} +} +"; + var src2 = @" +using System; + +class C +{ + static int F(Func x) => 1; + + int A = F(a => a + 1); + + public C(int x) {} + + public C() // new ctor + { + F(c => c + 1); + } +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "public C()")); + + // TODO (bug https://github.com/dotnet/roslyn/issues/2504): + //edits.VerifySemantics( + // ActiveStatementsDescription.Empty, + // new[] + // { + // SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").Constructors.Single(), syntaxMap[0]) + // }); + } + [Fact, WorkItem(2504, "https://github.com/dotnet/roslyn/issues/2504")] public void FieldInsert_ConstructorInsert_WithInitializersAndLambdas1() { @@ -7373,8 +9149,8 @@ public void FieldDelete1() "Delete [int a = 1]@10", "Delete [a = 1]@14"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "class C", FeaturesResources.field)); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))); } [Fact] @@ -7451,6 +9227,29 @@ public void EventFieldReorder() Diagnostic(RudeEditKind.Move, "event int c = 2", CSharpFeaturesResources.event_field)); } + [Fact] + public void EventField_Partial_InsertDelete() + { + var srcA1 = "partial class C { }"; + var srcB1 = "partial class C { event int E = 2; }"; + + var srcA2 = "partial class C { event int E = 2; }"; + var srcB2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) + }), + + DocumentResults(), + }); + } + #endregion #region Properties @@ -7471,6 +9270,20 @@ public void PropertyWithExpressionBody_Update() }); } + [Fact, WorkItem(48628, "https://github.com/dotnet/roslyn/issues/48628")] + public void PropertyWithExpressionBody_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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "unsafe int P", FeaturesResources.property_)); + } + [Fact] public void Property_ExpressionBodyToBlockBody1() { @@ -7545,7 +9358,7 @@ public void Property_BlockBodyToExpressionBody2() "Delete [set { }]@36"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int P", CSharpFeaturesResources.property_setter)); + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_setter, "P.set"))); } [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] @@ -7600,6 +9413,38 @@ public void Property_GetterBlockBodyToGetterExpressionBody() }); } + [Fact, WorkItem(17681, "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, new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod), + }); + } + + [Fact, WorkItem(17681, "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, new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod, preserveLocalVariables: false), + }); + } + [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] public void Property_GetterExpressionBodyToGetterBlockBody() { @@ -7705,7 +9550,7 @@ public void PropertyReorder2() } [Fact] - public void PropertyAccessorReorder() + public void PropertyAccessorReorder_GetSet() { var src1 = "class C { int P { get { return 1; } set { } } }"; var src2 = "class C { int P { set { } get { return 1; } } }"; @@ -7718,6 +9563,20 @@ public void PropertyAccessorReorder() edits.VerifyRudeDiagnostics(); } + [Fact] + public void PropertyAccessorReorder_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.VerifyRudeDiagnostics(); + } + [Fact] public void PropertyTypeUpdate() { @@ -7733,6 +9592,18 @@ public void PropertyTypeUpdate() Diagnostic(RudeEditKind.TypeUpdate, "char P", FeaturesResources.auto_property)); } + [Fact] + public void PropertyInsert() + { + 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("C").GetMember("P"))); + } + [WorkItem(835827, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")] [Fact] public void PropertyInsert_PInvoke() @@ -7750,18 +9621,22 @@ class C class C { - private static extern int P { [DllImport(""msvcrt.dll"")]get; } + 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.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InsertExtern, "private static extern int P", FeaturesResources.property_)); + 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_)); } [Fact] - public void Property_InsertIntoStruct() + public void PropertyInsert_IntoStruct() { var src1 = @" struct S @@ -7862,7 +9737,7 @@ public void PrivatePropertyAccessorDelete() edits.VerifyEdits("Delete [set { _p = value; }]@44"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int P", CSharpFeaturesResources.property_setter)); + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_setter, "P.set"))); } [Fact] @@ -7930,9 +9805,22 @@ public void PrivateAutoPropertyAccessorAdd6() edits.VerifyRudeDiagnostics(); } + [Fact] + public void PrivateAutoPropertyAccessorAdd_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.VerifyRudeDiagnostics(); + } + [WorkItem(755975, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755975")] [Fact] - public void PrivateAutoPropertyAccessorDelete1() + public void PrivateAutoPropertyAccessorDelete_Get() { var src1 = "class C { int P { get; set; } }"; var src2 = "class C { int P { set; } }"; @@ -7942,21 +9830,67 @@ public void PrivateAutoPropertyAccessorDelete1() edits.VerifyEdits("Delete [get;]@18"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int P", CSharpFeaturesResources.property_getter)); + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_getter, "P.get"))); + } + + [Fact] + public void AutoPropertyAccessor_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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.AccessorKindUpdate, "init", CSharpFeaturesResources.property_setter)); + } + + [Fact] + public void AutoPropertyAccessor_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.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.AccessorKindUpdate, "set", CSharpFeaturesResources.property_setter)); } [Fact] - public void PrivateAutoPropertyAccessorDelete2() + public void PrivateAutoPropertyAccessorDelete_Set() { var src1 = "class C { int P { get; set; } = 1; }"; - var src2 = "class C { int P { set; } = 1; }"; + var src2 = "class C { int P { get; } = 1; }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Delete [get;]@18"); + edits.VerifyEdits("Delete [set;]@23"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_setter, "P.set"))); + } + + [Fact] + public void PrivateAutoPropertyAccessorDelete_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.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int P", CSharpFeaturesResources.property_getter)); + Diagnostic(RudeEditKind.Delete, "int P", DeletedSymbolDisplay(CSharpFeaturesResources.property_setter, "P.init"))); } [Fact] @@ -7991,13 +9925,14 @@ public void InsertIncompleteProperty() public void Property_ReadOnlyRef_Insert() { var src1 = "class Test { }"; - var src2 = "class Test { ref readonly int M() { get; } }"; + var src2 = "class Test { ref readonly int P { get; } }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [ref readonly int M() { get; }]@13", - "Insert [()]@31"); + "Insert [ref readonly int P { get; }]@13", + "Insert [{ get; }]@32", + "Insert [get;]@34"); edits.VerifyRudeDiagnostics(); } @@ -8005,16 +9940,122 @@ public void Property_ReadOnlyRef_Insert() [Fact] public void Property_ReadOnlyRef_Update() { - var src1 = "class Test { int M() { get; } }"; - var src2 = "class Test { ref readonly int M() { get; } }"; + 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 M() { get; }]@13 -> [ref readonly int M() { get; }]@13"); + "Update [int P { get; }]@13 -> [ref readonly int P { get; }]@13"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int M()", FeaturesResources.method)); + Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int P", FeaturesResources.auto_property)); + } + + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("Q").GetMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("Q").SetMethod) + }), + + DocumentResults(), + }); + } + + [Fact] + public void AutoProperty_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 { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(), + }); + } + + [Fact] + public void AutoPropertyWithInitializer_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 { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) }), + + DocumentResults(), + }); + } + + [Fact] + public void PropertyWithExpressionBody_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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod) }), + + DocumentResults(), + }); } #endregion @@ -8055,6 +10096,23 @@ public void Indexer_SetterUpdate() }); } + [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, new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: false) + }); + } + [Fact] public void IndexerWithExpressionBody_Update() { @@ -8072,6 +10130,37 @@ public void IndexerWithExpressionBody_Update() }); } + [Fact, WorkItem(51297, "https://github.com/dotnet/roslyn/issues/51297")] + public void IndexerWithExpressionBody_Update_LiftedParameter() + { + // TODO: https://github.com/dotnet/roslyn/issues/51297 + // The test fails if "+ 10" and "+ 11" are removed. + + var src1 = @" +using System; + +class C +{ + int this[int a] => new Func(() => a + 1)() + 10; +} +"; + var src2 = @" +using System; + +class C +{ + int this[int a] => new Func(() => 2)() + 11; // not capturing a anymore +}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int this[int a] => new Func(() => a + 1)() + 10;]@35 -> [int this[int a] => new Func(() => 2)() + 11;]@35"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")); + } + [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] public void Indexer_ExpressionBodyToBlockBody() { @@ -8190,6 +10279,40 @@ public void Indexer_GetterBlockBodyToGetterExpressionBody() }); } + [Fact, WorkItem(17681, "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, new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")), + }); + } + + [Fact, WorkItem(17681, "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, new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item")), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")), + }); + } + [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] public void Indexer_GetterExpressionBodyToGetterBlockBody() { @@ -8221,7 +10344,7 @@ public void Indexer_GetterAndSetterBlockBodiesToExpressionBody() "Delete [set { Console.WriteLine(0); }]@46"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int this[int a]", CSharpFeaturesResources.indexer_setter)); + Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); } [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] @@ -8416,54 +10539,50 @@ public T this[int i] edits.VerifyEdits("Insert [get { return arr[i]; }]@304"); - edits.VerifyRudeDiagnostics(); + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InsertIntoGenericType, "get", CSharpFeaturesResources.indexer_getter)); } [Fact] public void Indexer_AddSetAccessor() { var src1 = @" -class Test -{ - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - System.Console.Write(stringCollection[0]); - } -} - -class SampleCollection +class C { - private T[] arr = new T[100]; - public T this[int i] - { - get { return arr[i]; } - } + public int this[int i] { get { return default; } } }"; var src2 = @" -class Test +class C { - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - System.Console.Write(stringCollection[0]); - } -} + public int this[int i] { get { return default; } set { } } +}"; + var edits = GetTopEdits(src1, src2); -class SampleCollection + edits.VerifyEdits("Insert [set { }]@67"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("this[]").SetMethod)); + } + + [Fact] + public void Indexer_AddSetAccessor_GenericType() + { + var src1 = @" +class C { - private T[] arr = new T[100]; - public T this[int i] - { - get { return arr[i]; } - set { arr[i] = value; } - } + public T this[int i] { get { return default; } } +}"; + var src2 = @" +class C +{ + public T this[int i] { get { return default; } set { } } }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Insert [set { arr[i] = value; }]@348"); + edits.VerifyEdits("Insert [set { }]@68"); - edits.VerifyRudeDiagnostics(); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoGenericType, "set", CSharpFeaturesResources.indexer_setter)); } [WorkItem(750109, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/750109")] @@ -8471,18 +10590,8 @@ public T this[int i] public void Indexer_DeleteGetAccessor() { var src1 = @" -class Test -{ - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - stringCollection[0] = ""hello""; - } -} - -class SampleCollection +class C { - private T[] arr = new T[100]; public T this[int i] { get { return arr[i]; } @@ -8490,18 +10599,8 @@ public T this[int i] } }"; var src2 = @" -class Test -{ - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - stringCollection[0] = ""hello""; - } -} - -class SampleCollection +class C { - private T[] arr = new T[100]; public T this[int i] { set { arr[i] = value; } @@ -8509,58 +10608,31 @@ public T this[int i] }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Delete [get { return arr[i]; }]@304"); + edits.VerifyEdits("Delete [get { return arr[i]; }]@58"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public T this[int i]", CSharpFeaturesResources.indexer_getter)); + Diagnostic(RudeEditKind.Delete, "public T this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_getter, "this[int].get"))); } [Fact] public void Indexer_DeleteSetAccessor() { var src1 = @" -class Test -{ - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - stringCollection[0] = ""hello""; - } -} - -class SampleCollection +class C { - private T[] arr = new T[100]; - public T this[int i] - { - get { return arr[i]; } - set { arr[i] = value; } - } + public int this[int i] { get { return 0; } set { } } }"; var src2 = @" -class Test -{ - static void Main(string[] args) - { - SampleCollection stringCollection = new SampleCollection(); - stringCollection[0] = ""hello""; - } -} - -class SampleCollection +class C { - private T[] arr = new T[100]; - public T this[int i] - { - get { return arr[i]; } - } + public int this[int i] { get { return 0; } } }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Delete [set { arr[i] = value; }]@336"); + edits.VerifyEdits("Delete [set { }]@61"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "public T this[int i]", CSharpFeaturesResources.indexer_setter)); + Diagnostic(RudeEditKind.Delete, "public int this[int i]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int].set"))); } [Fact, WorkItem(1174850, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1174850")] @@ -8678,6 +10750,107 @@ public void Indexer_ReadOnlyRef_ReturnType_Update() Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int this[int i]", FeaturesResources.indexer_)); } + [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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("this[]").GetMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("this[]").GetMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("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; } int Q { get; init; } }"; + + var srcA2 = "partial class C { int this[int x] { get; set; } int Q { get; init; } }"; + var srcB2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults(), + }); + } + + [Fact, WorkItem(51297, "https://github.com/dotnet/roslyn/issues/51297")] + public void IndexerWithExpressionBody_Partial_InsertDeleteUpdate_LiftedParameter() + { + // TODO: https://github.com/dotnet/roslyn/issues/51297 + // The test fails if "+ 10" and "+ 11" are removed. + + var srcA1 = @" +partial class C +{ +}"; + var srcB1 = @" +partial class C +{ + int this[int a] => new System.Func(() => a + 1) + 10; +}"; + + var srcA2 = @" +partial class C +{ + int this[int a] => new System.Func(() => 2) + 11; // no capture +}"; + var srcB2 = @" +partial class C +{ +}"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(diagnostics: new[] { Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a") }), + DocumentResults(), + }); + } + #endregion #region Events @@ -8725,6 +10898,30 @@ public void EventAccessorReorder3() "Reorder [remove { }]@33 -> @64"); } + [Fact] + public void EventInsert() + { + 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("C").GetMember("E"))); + } + + [Fact] + public void EventDelete() + { + var src1 = "class C { event int E { remove { } add { } } }"; + var src2 = "class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.event_, "E"))); + } + [Fact] public void EventInsert_IntoLayoutClass_Sequential() { @@ -8807,6 +11004,30 @@ public class C 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( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("E").AddMethod), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("E").RemoveMethod) + }), + + DocumentResults(), + }); + } + #endregion #region Parameter diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index e3767c42678d7..222dcec20922c 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -900,36 +900,28 @@ public async Task BreakMode_FileAdded() var service = CreateEditAndContinueService(workspace); StartDebuggingSession(service); - StartEditSession(service, loadedModules: new MockManagedEditAndContinueDebuggerService() - { - IsEditAndContinueAvailable = _ => new ManagedEditAndContinueAvailability(ManagedEditAndContinueAvailabilityStatus.NotSupportedForClr64Version, "*message*") - }); + StartEditSession(service); // add a source file: - var documentB = project.AddDocument("file2.cs", SourceText.From(sourceB), filePath: sourceFileB.Path); + var documentB = project.AddDocument("file2.cs", SourceText.From(sourceB, Encoding.UTF8), filePath: sourceFileB.Path); workspace.ChangeSolution(documentB.Project.Solution); + documentB = workspace.CurrentSolution.GetDocument(documentB.Id); var diagnostics2 = await service.GetDocumentDiagnosticsAsync(documentB, s_noDocumentActiveSpans, CancellationToken.None).ConfigureAwait(false); - AssertEx.Equal( - new[] { "ENC0071: " + string.Format(FeaturesResources.Adding_a_new_file_will_prevent_the_debug_session_from_continuing) }, - diagnostics2.Select(d => $"{d.Id}: {d.GetMessage()}")); + Assert.Empty(diagnostics2); Assert.True(await service.HasChangesAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, sourceFilePath: null, CancellationToken.None).ConfigureAwait(false)); var (updates, emitDiagnostics) = await service.EmitSolutionUpdateAsync(workspace.CurrentSolution, s_noSolutionActiveSpans, CancellationToken.None).ConfigureAwait(false); - Assert.Equal(ManagedModuleUpdateStatus.Blocked, updates.Status); - Assert.Empty(updates.Updates); - AssertEx.Equal(new[] { $"{project.Id} Error ENC2010: {string.Format(FeaturesResources.EditAndContinueDisallowedByProject, project.Name, "*message*")}" }, InspectDiagnostics(emitDiagnostics)); + Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); - EndEditSession(service, documentsWithRudeEdits: ImmutableArray.Create(documentB.Id)); + EndEditSession(service); EndDebuggingSession(service); AssertEx.Equal(new[] { "Debugging_EncSession: SessionId=1|SessionCount=1|EmptySessionCount=0", - "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=True|HadValidChanges=False|HadValidInsignificantChanges=False|RudeEditsCount=1|EmitDeltaErrorIdCount=1", - "Debugging_EncSession_EditSession_EmitDeltaErrorId: SessionId=1|EditSessionId=2|ErrorId=ENC2010", - "Debugging_EncSession_EditSession_RudeEdit: SessionId=1|EditSessionId=2|RudeEditKind=71|RudeEditSyntaxKind=0|RudeEditBlocking=True" + "Debugging_EncSession_EditSession: SessionId=1|EditSessionId=2|HadCompilationErrors=False|HadRudeEdits=False|HadValidChanges=True|HadValidInsignificantChanges=False|RudeEditsCount=0|EmitDeltaErrorIdCount=0" }, _telemetryLog); } diff --git a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs index cd9a2f015092c..4643bb76e6670 100644 --- a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs @@ -28,8 +28,6 @@ public void ToDiagnostic() { RudeEditKind.ActiveStatementUpdate, RudeEditKind.PartiallyExecutedActiveStatementUpdate, - RudeEditKind.PartiallyExecutedActiveStatementDelete, - RudeEditKind.DeleteActiveStatement, RudeEditKind.UpdateExceptionHandlerOfActiveTry, RudeEditKind.UpdateTryOrCatchWithActiveFinally, RudeEditKind.UpdateCatchHandlerAroundActiveStatement, @@ -39,7 +37,6 @@ public void ToDiagnostic() RudeEditKind.MethodKindUpdate, RudeEditKind.DeclareLibraryUpdate, RudeEditKind.DeclareAliasUpdate, - RudeEditKind.ChangingConstructorVisibility, RudeEditKind.InsertDllImport, RudeEditKind.MethodBodyAdd, RudeEditKind.MethodBodyDelete, diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/DocumentAnalysisResultsDescription.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/DocumentAnalysisResultsDescription.cs new file mode 100644 index 0000000000000..bb569665adeb3 --- /dev/null +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/DocumentAnalysisResultsDescription.cs @@ -0,0 +1,42 @@ +// 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. + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests +{ + internal readonly struct DocumentAnalysisResultsDescription + { + public readonly ActiveStatementsDescription ActiveStatements; + + /// + /// Default if semantic edits are not validated by the test. + /// + public readonly ImmutableArray SemanticEdits; + + public readonly ImmutableArray Diagnostics; + + public DocumentAnalysisResultsDescription( + ActiveStatementsDescription? activeStatements = null, + SemanticEditDescription[]? semanticEdits = null, + RudeEditDiagnosticDescription[]? diagnostics = null) + { + // The test must validate semantic edits, diagnostics or both. + // If neither is specified then assume the expectation is that + // the documents has no edits and no diagnostics. + if (semanticEdits is null && diagnostics is null) + { + SemanticEdits = ImmutableArray.Empty; + Diagnostics = ImmutableArray.Empty; + } + else + { + SemanticEdits = semanticEdits.AsImmutableOrNull(); + Diagnostics = diagnostics.AsImmutableOrEmpty(); + } + + ActiveStatements = activeStatements ?? ActiveStatementsDescription.Empty; + } + } +} diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index ce4c509d7be6a..14fc87873715e 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -5,10 +5,16 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Composition; +using System.Diagnostics; +using System.IO; using System.Linq; +using System.Text; using System.Threading; using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; @@ -26,8 +32,9 @@ internal abstract class EditAndContinueTestHelpers public abstract AbstractEditAndContinueAnalyzer Analyzer { get; } public abstract SyntaxNode FindNode(SyntaxNode root, TextSpan span); public abstract SyntaxTree ParseText(string source); - public abstract Compilation CreateLibraryCompilation(string name, IEnumerable trees); public abstract ImmutableArray GetDeclarators(ISymbol method); + public abstract string LanguageName { get; } + public abstract TreeComparer TopSyntaxComparer { get; } internal void VerifyUnchangedDocument( string source, @@ -57,92 +64,46 @@ internal void VerifyUnchangedDocument( actualNewExceptionRegions); // check active statements: - AssertSpansEqual(expectedNewActiveStatements, actualNewActiveStatements.Select(s => s.Span), source, text); + AssertSpansEqual(expectedNewActiveStatements, actualNewActiveStatements.Select(s => s.Span), text); // check new exception regions: Assert.Equal(expectedNewExceptionRegions.Length, actualNewExceptionRegions.Count); for (var i = 0; i < expectedNewExceptionRegions.Length; i++) { - AssertSpansEqual(expectedNewExceptionRegions[i], actualNewExceptionRegions[i], source, text); + AssertSpansEqual(expectedNewExceptionRegions[i], actualNewExceptionRegions[i], text); } } - internal void VerifyRudeDiagnostics( - EditScript editScript, + private void VerifyActiveStatementsAndExceptionRegions( ActiveStatementsDescription description, - RudeEditDiagnosticDescription[] expectedDiagnostics) + IReadOnlyList oldActiveStatements, + SourceText oldText, + SourceText newText, + SyntaxNode oldRoot, + IReadOnlyList actualNewActiveStatements, + IReadOnlyList> actualNewExceptionRegions) { - var oldActiveStatements = description.OldStatements; - - if (description.OldTrackingSpans != null) - { - Assert.Equal(oldActiveStatements.Length, description.OldTrackingSpans.Length); - } - - var newSource = editScript.Match.NewRoot.SyntaxTree.ToString(); - var oldSource = editScript.Match.OldRoot.SyntaxTree.ToString(); - - var oldText = SourceText.From(oldSource); - var newText = SourceText.From(newSource); - - var diagnostics = new ArrayBuilder(); - var updatedActiveMethodMatches = new ArrayBuilder(); - var actualNewActiveStatements = ImmutableArray.CreateBuilder(oldActiveStatements.Length); - actualNewActiveStatements.Count = actualNewActiveStatements.Capacity; - var actualNewExceptionRegions = ImmutableArray.CreateBuilder>(oldActiveStatements.Length); - actualNewExceptionRegions.Count = actualNewExceptionRegions.Capacity; - var editMap = BuildEditMap(editScript); - - var documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument"); - var testAccessor = Analyzer.GetTestAccessor(); - - testAccessor.AnalyzeMemberBodiesSyntax( - editScript, - editMap, - oldText, - newText, - oldActiveStatements.AsImmutable(), - description.OldTrackingSpans.ToImmutableArrayOrEmpty(), - actualNewActiveStatements, - actualNewExceptionRegions, - updatedActiveMethodMatches, - diagnostics); - - testAccessor.ReportTopLevelSynctactiveRudeEdits(diagnostics, editScript, editMap); - - diagnostics.Verify(newSource, expectedDiagnostics); - // check active statements: - AssertSpansEqual(description.NewSpans, actualNewActiveStatements.Select(s => s.Span), newSource, newText); + AssertSpansEqual(description.NewSpans, actualNewActiveStatements.Select(s => s.Span), newText); - if (diagnostics.Count == 0) + // check old exception regions: + for (var i = 0; i < oldActiveStatements.Count; i++) { - // check old exception regions: - for (var i = 0; i < oldActiveStatements.Length; i++) - { - var actualOldExceptionRegions = Analyzer.GetExceptionRegions( - oldText, - editScript.Match.OldRoot, - oldActiveStatements[i].Span, - isNonLeaf: oldActiveStatements[i].IsNonLeaf, - out _); - - AssertSpansEqual(description.OldRegions[i], actualOldExceptionRegions, oldSource, oldText); - } + var oldRegions = Analyzer.GetExceptionRegions( + oldText, + oldRoot, + oldActiveStatements[i].Span, + isNonLeaf: oldActiveStatements[i].IsNonLeaf, + out _); - // check new exception regions: - Assert.Equal(description.NewRegions.Length, actualNewExceptionRegions.Count); - for (var i = 0; i < description.NewRegions.Length; i++) - { - AssertSpansEqual(description.NewRegions[i], actualNewExceptionRegions[i], newSource, newText); - } + AssertSpansEqual(description.OldRegions[i], oldRegions, oldText); } - else + + // check new exception regions: + Assert.Equal(description.NewRegions.Length, actualNewExceptionRegions.Count); + for (var i = 0; i < description.NewRegions.Length; i++) { - for (var i = 0; i < oldActiveStatements.Length; i++) - { - Assert.Equal(0, description.NewRegions[i].Length); - } + AssertSpansEqual(description.NewRegions[i], actualNewExceptionRegions[i], newText); } } @@ -174,7 +135,7 @@ internal void VerifyLineEdits( diagnostics, default); - diagnostics.Verify(newSource, expectedDiagnostics); + VerifyDiagnostics(expectedDiagnostics, diagnostics, newText); AssertEx.Equal(expectedLineEdits, actualLineEdits, itemSeparator: ",\r\n"); @@ -182,102 +143,97 @@ internal void VerifyLineEdits( AssertEx.Equal(expectedNodeUpdates, actualNodeUpdates, itemSeparator: ",\r\n"); } - internal void VerifySemantics( - IEnumerable> editScripts, - ActiveStatementsDescription? activeStatements = null, - SemanticEditDescription[]? expectedSemanticEdits = null, - RudeEditDiagnosticDescription[]? expectedDiagnostics = null) + internal void VerifySemantics(EditScript[] editScripts, TargetFramework targetFramework, DocumentAnalysisResultsDescription[] expectedResults) { - activeStatements ??= ActiveStatementsDescription.Empty; + Assert.True(editScripts.Length == expectedResults.Length); + var documentCount = expectedResults.Length; - var oldTrees = editScripts.Select(editScript => editScript.Match.OldRoot.SyntaxTree).ToArray(); - var newTrees = editScripts.Select(editScript => editScript.Match.NewRoot.SyntaxTree).ToArray(); + using var workspace = new AdhocWorkspace(FeaturesTestCompositions.Features.GetHostServices()); + CreateProjects(editScripts, workspace, targetFramework, out var oldProject, out var newProject); - var oldCompilation = CreateLibraryCompilation("Old", oldTrees); - var newCompilation = CreateLibraryCompilation("New", newTrees); + var oldDocuments = oldProject.Documents.ToArray(); + var newDocuments = newProject.Documents.ToArray(); - var oldActiveStatements = activeStatements.OldStatements.AsImmutable(); - var triviaEdits = new ArrayBuilder<(SyntaxNode OldNode, SyntaxNode NewNode)>(); - var actualLineEdits = new ArrayBuilder(); - var actualSemanticEdits = new ArrayBuilder(); - var includeFirstLineInDiagnostics = expectedDiagnostics?.Any(d => d.FirstLine != null) == true; - var actualDiagnosticDescriptions = new ArrayBuilder(); - var actualDeclarationErrors = new ArrayBuilder(); - - var actualNewActiveStatements = ImmutableArray.CreateBuilder(activeStatements.OldStatements.Length); - actualNewActiveStatements.Count = actualNewActiveStatements.Capacity; + Debug.Assert(oldDocuments.Length == newDocuments.Length); - var actualNewExceptionRegions = ImmutableArray.CreateBuilder>(activeStatements.OldStatements.Length); - actualNewExceptionRegions.Count = actualNewExceptionRegions.Capacity; + var oldTrees = oldDocuments.Select(d => d.GetSyntaxTreeSynchronously(default)!).ToArray(); + var newTrees = newDocuments.Select(d => d.GetSyntaxTreeSynchronously(default)!).ToArray(); var testAccessor = Analyzer.GetTestAccessor(); - foreach (var editScript in editScripts) + for (var documentIndex = 0; documentIndex < documentCount; documentIndex++) { - var oldRoot = editScript.Match.OldRoot; - var newRoot = editScript.Match.NewRoot; - var oldSource = oldRoot.SyntaxTree.ToString(); - var newSource = newRoot.SyntaxTree.ToString(); - - var editMap = BuildEditMap(editScript); - var oldText = SourceText.From(oldSource); - var newText = SourceText.From(newSource); - var oldModel = oldCompilation.GetSemanticModel(oldRoot.SyntaxTree); - var newModel = newCompilation.GetSemanticModel(newRoot.SyntaxTree); - - var diagnostics = new ArrayBuilder(); - var updatedActiveMethodMatches = new ArrayBuilder(); - - testAccessor.AnalyzeMemberBodiesSyntax( - editScript, - editMap, - oldText, - newText, - oldActiveStatements, - activeStatements.OldTrackingSpans.ToImmutableArrayOrEmpty(), - actualNewActiveStatements, - actualNewExceptionRegions, - updatedActiveMethodMatches, - diagnostics); + var expectedResult = expectedResults[documentIndex]; - testAccessor.ReportTopLevelSynctactiveRudeEdits(diagnostics, editScript, editMap); + var oldActiveStatements = expectedResult.ActiveStatements.OldStatements.ToImmutableArray(); - testAccessor.AnalyzeTrivia( - oldText, - newText, - editScript.Match, - editMap, - triviaEdits, - actualLineEdits, - diagnostics, - CancellationToken.None); - - testAccessor.AnalyzeSemantics( - editScript, - editMap, - oldText, - oldActiveStatements, - triviaEdits, - updatedActiveMethodMatches, - oldModel, - newModel, - actualSemanticEdits, - diagnostics, - CancellationToken.None); - - actualDiagnosticDescriptions.AddRange(diagnostics.ToDescription(newSource, includeFirstLineInDiagnostics)); - } + var includeFirstLineInDiagnostics = expectedResult.Diagnostics.Any(d => d.FirstLine != null) == true; + var newActiveStatementSpans = expectedResult.ActiveStatements.OldTrackingSpans.ToImmutableArrayOrEmpty(); - actualDiagnosticDescriptions.Verify(expectedDiagnostics); + // we need to rebuild the edit script, so that it operates on nodes associated with the same syntax trees backing the documents: + var oldTree = oldTrees[documentIndex]; + var newTree = newTrees[documentIndex]; + var oldRoot = oldTree.GetRoot(); + var newRoot = newTree.GetRoot(); - if (expectedSemanticEdits == null) - { - return; + var oldDocument = oldDocuments[documentIndex]; + var newDocument = newDocuments[documentIndex]; + + var oldModel = oldDocument.GetSemanticModelAsync().Result; + var newModel = newDocument.GetSemanticModelAsync().Result; + Contract.ThrowIfNull(oldModel); + Contract.ThrowIfNull(newModel); + + var result = Analyzer.AnalyzeDocumentAsync(oldProject, oldActiveStatements, newDocument, newActiveStatementSpans, CancellationToken.None).Result; + + var oldText = oldDocument.GetTextSynchronously(default); + var newText = newDocument.GetTextSynchronously(default); + + VerifyDiagnostics(expectedResult.Diagnostics, result.RudeEditErrors.ToDescription(newText, includeFirstLineInDiagnostics)); + + if (!expectedResult.SemanticEdits.IsDefault) + { + VerifySemanticEdits(expectedResult.SemanticEdits, result.SemanticEdits, oldModel.Compilation, newModel.Compilation, oldRoot, newRoot); + } + + if (expectedResult.Diagnostics.IsEmpty) + { + VerifyActiveStatementsAndExceptionRegions( + expectedResult.ActiveStatements, + oldActiveStatements, + oldText, + newText, + oldRoot, + result.ActiveStatements, + result.ExceptionRegions); + } + else + { + Assert.True(result.ExceptionRegions.IsDefault); + } } + } - Assert.Equal(expectedSemanticEdits.Length, actualSemanticEdits.Count); + public static void VerifyDiagnostics(IEnumerable expected, IEnumerable actual, SourceText newSource) + => VerifyDiagnostics(expected, actual.ToDescription(newSource, expected.Any(d => d.FirstLine != null))); - for (var i = 0; i < actualSemanticEdits.Count; i++) + public static void VerifyDiagnostics(IEnumerable expected, IEnumerable actual) + => AssertEx.SetEqual(expected, actual, itemSeparator: ",\r\n"); + + private void VerifySemanticEdits( + ImmutableArray expectedSemanticEdits, + ImmutableArray actualSemanticEdits, + Compilation oldCompilation, + Compilation newCompilation, + SyntaxNode oldRoot, + SyntaxNode newRoot) + { + // string comparison to simplify understanding why a test failed: + AssertEx.Equal( + expectedSemanticEdits.Select(e => $"{e.Kind}: {e.SymbolProvider(newCompilation)}"), + actualSemanticEdits.NullToEmpty().Select(e => $"{e.Kind}: {e.Symbol.Resolve(newCompilation).Symbol}")); + + for (var i = 0; i < actualSemanticEdits.Length; i++) { var editKind = expectedSemanticEdits[i].Kind; @@ -285,24 +241,32 @@ internal void VerifySemantics( var expectedOldSymbol = (editKind == SemanticEditKind.Update) ? expectedSemanticEdits[i].SymbolProvider(oldCompilation) : null; var expectedNewSymbol = expectedSemanticEdits[i].SymbolProvider(newCompilation); - var actualOldSymbol = actualSemanticEdits[i].OldSymbol; - var actualNewSymbol = actualSemanticEdits[i].NewSymbol; + var symbolKey = actualSemanticEdits[i].Symbol; - Assert.Equal(expectedOldSymbol, actualOldSymbol); - Assert.Equal(expectedNewSymbol, actualNewSymbol); + if (editKind == SemanticEditKind.Update) + { + Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol); + Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); + } + else if (editKind == SemanticEditKind.Insert) + { + Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); + } + else + { + Assert.False(true, "Only Update or Insert allowed"); + } - var expectedSyntaxMap = expectedSemanticEdits[i].SyntaxMap; - var syntaxTreeOrdinal = expectedSemanticEdits[i].SyntaxTreeOrdinal; - var oldRoot = oldTrees[syntaxTreeOrdinal].GetRoot(); - var newRoot = newTrees[syntaxTreeOrdinal].GetRoot(); + // Edit is expected to have a syntax map: var actualSyntaxMap = actualSemanticEdits[i].SyntaxMap; + Assert.Equal(expectedSemanticEdits[i].HasSyntaxMap, actualSyntaxMap != null); - Assert.Equal(expectedSemanticEdits[i].PreserveLocalVariables, actualSemanticEdits[i].PreserveLocalVariables); + // If expected map is specified validate its mappings with the actual one: + var expectedSyntaxMap = expectedSemanticEdits[i].SyntaxMap; if (expectedSyntaxMap != null) { Contract.ThrowIfNull(actualSyntaxMap); - Assert.True(expectedSemanticEdits[i].PreserveLocalVariables); foreach (var expectedSpanMapping in expectedSyntaxMap) { @@ -313,24 +277,41 @@ internal void VerifySemantics( Assert.Equal(expectedOldNode, actualOldNode); } } - else if (!expectedSemanticEdits[i].PreserveLocalVariables) - { - Assert.Null(actualSyntaxMap); - } } } - private static void AssertSpansEqual(IEnumerable expected, IEnumerable actual, string newSource, SourceText newText) + private void CreateProjects(EditScript[] editScripts, AdhocWorkspace workspace, TargetFramework targetFramework, out Project oldProject, out Project newProject) + { + oldProject = workspace.AddProject("project", LanguageName).WithMetadataReferences(TargetFrameworkUtil.GetReferences(targetFramework)); + var documentIndex = 0; + foreach (var editScript in editScripts) + { + oldProject = oldProject.AddDocument(documentIndex.ToString(), editScript.Match.OldRoot).Project; + documentIndex++; + } + + var newSolution = oldProject.Solution; + documentIndex = 0; + foreach (var oldDocument in oldProject.Documents) + { + newSolution = newSolution.WithDocumentSyntaxRoot(oldDocument.Id, editScripts[documentIndex].Match.NewRoot, PreservationMode.PreserveIdentity); + documentIndex++; + } + + newProject = newSolution.Projects.Single(); + } + + private static void AssertSpansEqual(IEnumerable expected, IEnumerable actual, SourceText newText) { AssertEx.Equal( expected, actual.Select(span => newText.Lines.GetTextSpan(span)), itemSeparator: "\r\n", - itemInspector: s => DisplaySpan(newSource, s)); + itemInspector: s => DisplaySpan(newText, s)); } - private static string DisplaySpan(string source, TextSpan span) - => span + ": [" + source.Substring(span.Start, span.Length).Replace("\r\n", " ") + "]"; + private static string DisplaySpan(SourceText source, TextSpan span) + => span + ": [" + source.GetSubText(span).ToString().Replace("\r\n", " ") + "]"; internal static IEnumerable> GetMethodMatches(AbstractEditAndContinueAnalyzer analyzer, Match bodyMatch) { diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/Extensions.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/Extensions.cs index dda94c6ed5806..797ba244a68da 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/Extensions.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/Extensions.cs @@ -7,39 +7,24 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests { internal static class Extensions { - public static void Verify(this IEnumerable diagnostics, string newSource, params RudeEditDiagnosticDescription[] expectedDiagnostics) - => diagnostics.ToDescription(newSource, expectedDiagnostics.Any(d => d.FirstLine != null)).Verify(expectedDiagnostics); - - public static void Verify(this IEnumerable diagnostics, params RudeEditDiagnosticDescription[] expectedDiagnostics) - { - expectedDiagnostics ??= Array.Empty(); - AssertEx.SetEqual(expectedDiagnostics, diagnostics, itemSeparator: ",\r\n"); - } - - public static IEnumerable ToDescription(this IEnumerable diagnostics, string newSource, bool includeFirstLines) + public static IEnumerable ToDescription(this IEnumerable diagnostics, SourceText newSource, bool includeFirstLines) { return diagnostics.Select(d => new RudeEditDiagnosticDescription( d.Kind, - d.Span == default ? null : newSource.Substring(d.Span.Start, d.Span.Length), + d.Span == default ? null : newSource.ToString(d.Span), d.Arguments, - firstLine: includeFirstLines ? GetLineAt(newSource, d.Span.Start) : null)); + firstLine: includeFirstLines ? newSource.Lines.GetLineFromPosition(d.Span.Start).ToString().Trim() : null)); } private const string LineSeparator = "\r\n"; - private static string GetLineAt(string source, int position) - { - var start = source.LastIndexOf(LineSeparator, position, position); - var end = source.IndexOf(LineSeparator, position); - return source.Substring(start + 1, end - start).Trim(); - } - public static IEnumerable ToLines(this string str) { var i = 0; diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/SemanticEditDescription.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/SemanticEditDescription.cs index ab646174bfa5a..840e9377ec21a 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/SemanticEditDescription.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/SemanticEditDescription.cs @@ -13,22 +13,27 @@ public sealed class SemanticEditDescription { public readonly SemanticEditKind Kind; public readonly Func SymbolProvider; - public readonly IEnumerable> SyntaxMap; - public readonly bool PreserveLocalVariables; - public readonly int SyntaxTreeOrdinal; + public readonly Func? PartialType; + + /// + /// If specified the node mappings will be validated against the actual syntax map function. + /// + public readonly IEnumerable>? SyntaxMap; + + public readonly bool HasSyntaxMap; public SemanticEditDescription( SemanticEditKind kind, Func symbolProvider, - IEnumerable> syntaxMap, - bool preserveLocalVariables, - int syntaxTreeOrdinal = 0) + Func? partialType, + IEnumerable>? syntaxMap, + bool hasSyntaxMap) { Kind = kind; SymbolProvider = symbolProvider; SyntaxMap = syntaxMap; - PreserveLocalVariables = preserveLocalVariables; - SyntaxTreeOrdinal = syntaxTreeOrdinal; + PartialType = partialType; + HasSyntaxMap = hasSyntaxMap; } } } diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index 529323c16a599..ab29de1e29ad4 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -6,6 +6,7 @@ Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests + Public Class ActiveStatementTests Inherits EditingTestBase @@ -263,7 +264,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo(Integer)"))) End Sub @@ -283,7 +284,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, Nothing, FeaturesResources.class_)) + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.class_, "C"))) End Sub @@ -316,7 +317,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Shared Sub Main()")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Shared Sub Main()", FeaturesResources.code)) End Sub @@ -421,21 +422,21 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Do"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "If True"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Else"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Case 1, 2"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Case Else"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "While True"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Do Until True"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "If True Then"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Else"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "For i = 0 To 10"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "For Each i In {1, 2}"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Using z = New C()"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "With expr"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "Shared Sub Main()"), - Diagnostic(RudeEditKind.DeleteActiveStatement, "SyncLock Nothing")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Do", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "If True", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Else", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Case 1, 2", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Case Else", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "While True", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Do Until True", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "If True Then", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Else", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "For i = 0 To 10", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "For Each i In {1, 2}", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Using z = New C()", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "With expr", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "Shared Sub Main()", FeaturesResources.code), + Diagnostic(RudeEditKind.DeleteActiveStatement, "SyncLock Nothing", FeaturesResources.code)) End Sub @@ -476,7 +477,7 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "If c1 Then")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "If c1 Then", FeaturesResources.code)) End Sub @@ -513,7 +514,7 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "If c1 Then")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "If c1 Then", FeaturesResources.code)) End Sub @@ -648,7 +649,7 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Sub Main()")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Sub Main()", FeaturesResources.code)) End Sub @@ -696,7 +697,7 @@ End Module Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, Nothing, "module")) + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(VBFeaturesResources.module_, "Module1"))) End Sub #End Region @@ -1652,8 +1653,8 @@ End Class edits.VerifyRudeDiagnostics(active, Diagnostic(RudeEditKind.ActiveStatementUpdate, "c As New D(2)"), Diagnostic(RudeEditKind.ActiveStatementUpdate, "e As New D(2)"), - Diagnostic(RudeEditKind.Delete, "a As New D(2)", FeaturesResources.field), - Diagnostic(RudeEditKind.Delete, "e As New D(2)", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "a As New D(2)", DeletedSymbolDisplay(FeaturesResources.field, "b")), + Diagnostic(RudeEditKind.Delete, "e As New D(2)", DeletedSymbolDisplay(FeaturesResources.field, "f"))) End Sub @@ -1775,12 +1776,12 @@ End Class Public Sub Initializer_Array_Update3() Dim src1 = " Class C - Private a(1) - Private e(1) - Private f(1) + Private a(1,0) + Private e(1,0) + Private f(1,0) Sub Main - Dim c(1) + Dim c(1,0) End Sub End Class " @@ -1800,11 +1801,8 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.ActiveStatementUpdate, "c(1,2)"), - Diagnostic(RudeEditKind.TypeUpdate, "a(1,2)", FeaturesResources.field), Diagnostic(RudeEditKind.ActiveStatementUpdate, "e(1,2)"), - Diagnostic(RudeEditKind.TypeUpdate, "e(1,2)", FeaturesResources.field), - Diagnostic(RudeEditKind.TypeUpdate, "f(1,2)", FeaturesResources.field)) + Diagnostic(RudeEditKind.ActiveStatementUpdate, "c(1,2)")) End Sub @@ -2091,7 +2089,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))) End Sub @@ -2144,7 +2142,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "a, c As New D()", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "a, c As New D()", DeletedSymbolDisplay(FeaturesResources.field, "b"))) End Sub @@ -2171,7 +2169,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "a, b As New D()", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "a, b As New D()", DeletedSymbolDisplay(FeaturesResources.field, "c"))) End Sub @@ -2225,7 +2223,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.Delete, "a,b As Integer", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "a,b As Integer", DeletedSymbolDisplay(FeaturesResources.field, "c"))) End Sub @@ -2328,7 +2326,7 @@ End Class Class C Dim a As Integer = 1 Shared b As Integer = 1 - Dim c(1) As Integer = 1 + Dim c(1) As Integer End Class " @@ -2859,7 +2857,7 @@ Class Test For Each b In e1 For Each c In e1 Dim a = Sub() - For Each a In e1 + For Each z In e1 System.Console.Write() Next End Sub @@ -2874,7 +2872,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.InsertAroundActiveStatement, "For Each a In e1", VBFeaturesResources.For_Each_block), + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "For Each z In e1", VBFeaturesResources.For_Each_block), Diagnostic(RudeEditKind.InsertAroundActiveStatement, "For Each b In e1", VBFeaturesResources.For_Each_block)) End Sub @@ -3497,7 +3495,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Try")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Try", FeaturesResources.code)) End Sub @@ -3561,7 +3559,7 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Try")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Try", FeaturesResources.code)) End Sub @@ -5294,7 +5292,7 @@ Imports System Imports System.Threading.Tasks Class C Sub F() - Dim f = Function(a) Function(b) a + b + Dim f = Function(a) Task.FromResult(Function(b) 1) End Sub End Class " @@ -5303,7 +5301,7 @@ Imports System Imports System.Threading.Tasks Class C Sub F() - Dim f = Async Function(a) Function(b) a + b + Dim f = Async Function(a) Function(b) 1 End Sub End Class " @@ -5322,7 +5320,7 @@ Imports System Imports System.Threading.Tasks Class C Sub F() - Dim f = Function(a) Function(b) a + b + Dim f = Function(a) Task.FromResult(Function(b) a + b) End Sub End Class " @@ -5362,7 +5360,7 @@ Class C End Function Sub F() - Dim f = Iterator Function() + Dim f = Iterator Function() As IEnumerable(Of Integer) G() End Function End Sub @@ -5372,14 +5370,14 @@ End Class Dim active = GetActiveStatements(src1, src2) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, "Iterator Function()")) + Diagnostic(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, "Iterator Function() As IEnumerable(Of Integer)")) End Sub Public Sub AsyncLambdaToLambda_WithoutActiveStatement_NoAwait() Dim src1 = " Class C - Function G() As Task(Of Integer) + Shared Function G() As Task(Of Integer) Return Nothing End Function @@ -5391,7 +5389,7 @@ End Class " Dim src2 = " Class C - Function G() As Task(Of Integer) + Shared Function G() As Task(Of Integer) Return Nothing End Function @@ -5415,7 +5413,7 @@ End Class Imports System Imports System.Threading.Tasks Class C - Function G() As IEnumerable(Of Integer) + Shared Function G() As IEnumerable(Of Integer) Return Nothing End Function @@ -5429,7 +5427,7 @@ End Class Imports System Imports System.Threading.Tasks Class C - Function G() As IEnumerable(Of Integer) + Shared Function G() As IEnumerable(Of Integer) Return Nothing End Function @@ -5715,7 +5713,7 @@ End Class active.OldStatements(0) = active.OldStatements(0).WithFlags(ActiveStatementFlags.PartiallyExecuted Or ActiveStatementFlags.IsLeafFrame) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementDelete, "Sub F()")) + Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementDelete, "Sub F()", FeaturesResources.code)) End Sub @@ -5739,7 +5737,7 @@ End Class active.OldStatements(0) = active.OldStatements(0).WithFlags(ActiveStatementFlags.IsNonLeafFrame Or ActiveStatementFlags.IsLeafFrame) edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.DeleteActiveStatement, "Sub F()")) + Diagnostic(RudeEditKind.DeleteActiveStatement, "Sub F()", FeaturesResources.code)) End Sub End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTrackingServiceTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTrackingServiceTests.vb index 585d77ec92c71..a3d85ad5e6c28 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTrackingServiceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTrackingServiceTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests + Public Class ActiveStatementTrackingServiceTests Inherits EditingTestBase diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb index 094e3b79bf762..a056621841930 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditAndContinueValidation.vb @@ -17,7 +17,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests source As String, description As ActiveStatementsDescription) - VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyUnchangedDocument( + Dim validator = New VisualBasicEditAndContinueTestHelpers() + validator.VerifyUnchangedDocument( ActiveStatementsDescription.ClearTags(source), description.OldStatements, description.NewSpans, @@ -34,7 +35,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Friend Sub VerifyRudeDiagnostics(editScript As EditScript(Of SyntaxNode), description As ActiveStatementsDescription, ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyRudeDiagnostics(editScript, description, expectedDiagnostics) + VerifySemantics(editScript, description, diagnostics:=expectedDiagnostics) End Sub @@ -42,33 +43,48 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests expectedLineEdits As IEnumerable(Of SourceLineUpdate), expectedNodeUpdates As IEnumerable(Of String), ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifyLineEdits(editScript, expectedLineEdits, expectedNodeUpdates, expectedDiagnostics) + Dim validator = New VisualBasicEditAndContinueTestHelpers() + validator.VerifyLineEdits(editScript, expectedLineEdits, expectedNodeUpdates, expectedDiagnostics) End Sub Friend Sub VerifySemanticDiagnostics(editScript As EditScript(Of SyntaxNode), ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VerifySemantics({editScript}, ActiveStatementsDescription.Empty, Nothing, expectedDiagnostics) + VerifySemantics( + {editScript}, + {New DocumentAnalysisResultsDescription(diagnostics:=expectedDiagnostics)}) + End Sub + + + Friend Sub VerifySemanticDiagnostics(editScript As EditScript(Of SyntaxNode), + targetFrameworks As TargetFramework(), + ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) + VerifySemantics( + {editScript}, + {New DocumentAnalysisResultsDescription(diagnostics:=expectedDiagnostics)}, + targetFrameworks:=targetFrameworks) End Sub Friend Sub VerifySemantics(editScript As EditScript(Of SyntaxNode), - activeStatements As ActiveStatementsDescription, - expectedSemanticEdits As SemanticEditDescription(), - ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VerifySemantics({editScript}, activeStatements, expectedSemanticEdits, expectedDiagnostics) + Optional activeStatements As ActiveStatementsDescription = Nothing, + Optional semanticEdits As SemanticEditDescription() = Nothing, + Optional diagnostics As RudeEditDiagnosticDescription() = Nothing, + Optional targetFrameworks As TargetFramework() = Nothing) + VerifySemantics( + {editScript}, + {New DocumentAnalysisResultsDescription(activeStatements, semanticEdits, diagnostics)}, + targetFrameworks) End Sub Friend Sub VerifySemantics(editScripts As EditScript(Of SyntaxNode)(), - Optional activeStatements As ActiveStatementsDescription = Nothing, - Optional expectedSemanticEdits As SemanticEditDescription() = Nothing, - Optional expectedDiagnostics As RudeEditDiagnosticDescription() = Nothing) - VisualBasicEditAndContinueTestHelpers.CreateInstance().VerifySemantics( - editScripts, - activeStatements, - expectedSemanticEdits, - expectedDiagnostics) + expected As DocumentAnalysisResultsDescription(), + Optional targetFrameworks As TargetFramework() = Nothing) + For Each framework In If(targetFrameworks, {TargetFramework.NetStandard20, TargetFramework.NetCoreApp}) + Dim validator = New VisualBasicEditAndContinueTestHelpers() + validator.VerifySemantics(editScripts, framework, expected) + Next End Sub End Module End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb index 306bf1acadef7..e22a1a8c89dc8 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb @@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.CodeAnalysis.PooledObjects Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests @@ -20,27 +21,56 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Return New VisualBasicEditAndContinueAnalyzer() End Function - Public Enum StateMachineKind - None + Public Enum MethodKind + Regular Async Iterator End Enum + Friend Shared NoSemanticEdits As SemanticEditDescription() = Array.Empty(Of SemanticEditDescription) + Friend Overloads Shared Function Diagnostic(rudeEditKind As RudeEditKind, squiggle As String, ParamArray arguments As String()) As RudeEditDiagnosticDescription Return New RudeEditDiagnosticDescription(rudeEditKind, squiggle, arguments, firstLine:=Nothing) End Function - Friend Shared Function SemanticEdit(kind As SemanticEditKind, symbolProvider As Func(Of Compilation, ISymbol), syntaxMap As IEnumerable(Of KeyValuePair(Of TextSpan, TextSpan))) As SemanticEditDescription - Assert.NotNull(syntaxMap) - Return New SemanticEditDescription(kind, symbolProvider, syntaxMap, preserveLocalVariables:=True) + Friend Shared Function SemanticEdit(kind As SemanticEditKind, + symbolProvider As Func(Of Compilation, ISymbol), + syntaxMap As IEnumerable(Of KeyValuePair(Of TextSpan, TextSpan)), + Optional partialType As String = Nothing) As SemanticEditDescription + Return New SemanticEditDescription( + kind, + symbolProvider, + If(partialType Is Nothing, Nothing, Function(c As Compilation) CType(c.GetMember(partialType), ITypeSymbol)), + syntaxMap:=Nothing, + hasSyntaxMap:=syntaxMap IsNot Nothing) + End Function + + Friend Shared Function SemanticEdit(kind As SemanticEditKind, + symbolProvider As Func(Of Compilation, ISymbol), + Optional partialType As String = Nothing, + Optional preserveLocalVariables As Boolean = False) As SemanticEditDescription + Return New SemanticEditDescription( + kind, + symbolProvider, + If(partialType Is Nothing, Nothing, Function(c As Compilation) CType(c.GetMember(partialType), ITypeSymbol)), + syntaxMap:=Nothing, + hasSyntaxMap:=preserveLocalVariables) + End Function + + Friend Shared Function DeletedSymbolDisplay(kind As String, displayName As String) As String + Return String.Format(FeaturesResources.member_kind_and_name, kind, displayName) End Function - Friend Shared Function SemanticEdit(kind As SemanticEditKind, symbolProvider As Func(Of Compilation, ISymbol), Optional preserveLocalVariables As Boolean = False) As SemanticEditDescription - Return New SemanticEditDescription(kind, symbolProvider, Nothing, preserveLocalVariables) + Friend Shared Function DocumentResults( + Optional activeStatements As ActiveStatementsDescription = Nothing, + Optional semanticEdits As SemanticEditDescription() = Nothing, + Optional diagnostics As RudeEditDiagnosticDescription() = Nothing) As DocumentAnalysisResultsDescription + Return New DocumentAnalysisResultsDescription(activeStatements, semanticEdits, diagnostics) End Function Private Shared Function ParseSource(source As String) As SyntaxTree - Return VisualBasicEditAndContinueTestHelpers.CreateInstance().ParseText(ActiveStatementsDescription.ClearTags(source)) + Dim validator = New VisualBasicEditAndContinueTestHelpers() + Return validator.ParseText(ActiveStatementsDescription.ClearTags(source)) End Function Friend Shared Function GetTopEdits(src1 As String, src2 As String) As EditScript(Of SyntaxNode) @@ -54,14 +84,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Return match.GetTreeEdits() End Function - Friend Shared Function GetMethodEdits(src1 As String, src2 As String, Optional stateMachine As StateMachineKind = StateMachineKind.None) As EditScript(Of SyntaxNode) - Dim match = GetMethodMatch(src1, src2, stateMachine) + Public Shared Function GetTopEdits(methodEdits As EditScript(Of SyntaxNode)) As EditScript(Of SyntaxNode) + Dim oldMethodSource = methodEdits.Match.OldRoot.ToFullString() + Dim newMethodSource = methodEdits.Match.NewRoot.ToFullString() + + Return GetTopEdits(WrapMethodBodyWithClass(oldMethodSource), WrapMethodBodyWithClass(newMethodSource)) + End Function + + Friend Shared Function GetMethodEdits(src1 As String, src2 As String, Optional methodKind As MethodKind = MethodKind.Regular) As EditScript(Of SyntaxNode) + Dim match = GetMethodMatch(src1, src2, methodKind) Return match.GetTreeEdits() End Function - Friend Shared Function GetMethodMatch(src1 As String, src2 As String, Optional stateMachine As StateMachineKind = StateMachineKind.None) As Match(Of SyntaxNode) - Dim m1 = MakeMethodBody(src1, stateMachine) - Dim m2 = MakeMethodBody(src2, stateMachine) + Friend Shared Function GetMethodMatch(src1 As String, src2 As String, Optional methodKind As MethodKind = MethodKind.Regular) As Match(Of SyntaxNode) + Dim m1 = MakeMethodBody(src1, methodKind) + Dim m2 = MakeMethodBody(src2, methodKind) Dim diagnostics = New ArrayBuilder(Of RudeEditDiagnostic)() @@ -69,9 +106,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Dim match = CreateAnalyzer().GetTestAccessor().ComputeBodyMatch(m1, m2, Array.Empty(Of AbstractEditAndContinueAnalyzer.ActiveNode)(), diagnostics, oldHasStateMachineSuspensionPoint, newHasStateMachineSuspensionPoint) Dim needsSyntaxMap = oldHasStateMachineSuspensionPoint AndAlso newHasStateMachineSuspensionPoint - Assert.Equal(stateMachine <> StateMachineKind.None, needsSyntaxMap) + Assert.Equal(methodKind <> MethodKind.Regular, needsSyntaxMap) - If stateMachine = StateMachineKind.None Then + If methodKind = MethodKind.Regular Then Assert.Empty(diagnostics) End If @@ -80,7 +117,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Public Shared Function GetMethodMatches(src1 As String, src2 As String, - Optional stateMachine As StateMachineKind = StateMachineKind.None) As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode)) + Optional stateMachine As MethodKind = MethodKind.Regular) As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode)) Dim methodMatch = GetMethodMatch(src1, src2, stateMachine) Return EditAndContinueTestHelpers.GetMethodMatches(CreateAnalyzer(), methodMatch) End Function @@ -93,18 +130,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Return EditAndContinueTestHelpers.ToMatchingPairs(matches) End Function - Friend Shared Function MakeMethodBody(bodySource As String, Optional stateMachine As StateMachineKind = StateMachineKind.None) As SyntaxNode - Dim source As String - Select Case stateMachine - Case StateMachineKind.Iterator - source = "Class C" & vbLf & "Iterator Function F() As IEnumerable(Of Integer)" & vbLf & bodySource & " : End Function : End Class" - - Case StateMachineKind.Async - source = "Class C" & vbLf & "Async Function F() As Task(Of Integer)" & vbLf & bodySource & " : End Function : End Class" - - Case Else - source = "Class C" & vbLf & "Sub F()" & vbLf & bodySource & " : End Sub : End Class" - End Select + Friend Shared Function MakeMethodBody(bodySource As String, Optional stateMachine As MethodKind = MethodKind.Regular) As SyntaxNode + Dim source = WrapMethodBodyWithClass(bodySource, stateMachine) Dim tree = ParseSource(source) Dim root = tree.GetRoot() @@ -112,7 +139,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Dim declaration = DirectCast(DirectCast(root, CompilationUnitSyntax).Members(0), ClassBlockSyntax).Members(0) Return SyntaxFactory.SyntaxTree(declaration).GetRoot() + End Function + Private Shared Function WrapMethodBodyWithClass(bodySource As String, Optional kind As MethodKind = MethodKind.Regular) As String + Select Case kind + Case MethodKind.Iterator + Return "Class C" & vbLf & "Iterator Function F() As IEnumerable(Of Integer)" & vbLf & bodySource & " : End Function : End Class" + + Case MethodKind.Async + Return "Class C" & vbLf & "Async Function F() As Task(Of Integer)" & vbLf & bodySource & " : End Function : End Class" + + Case Else + Return "Class C" & vbLf & "Sub F()" & vbLf & bodySource & " : End Sub : End Class" + End Select End Function Friend Shared Function GetActiveStatements(oldSource As String, newSource As String) As ActiveStatementsDescription diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb index 354f4f122e4b4..a8efa33be4acf 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestHelpers.vb @@ -8,33 +8,17 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.Differencing Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Friend NotInheritable Class VisualBasicEditAndContinueTestHelpers Inherits EditAndContinueTestHelpers - Private ReadOnly _analyzer As VisualBasicEditAndContinueAnalyzer = New VisualBasicEditAndContinueAnalyzer() + Private ReadOnly _analyzer As VisualBasicEditAndContinueAnalyzer - Private ReadOnly _fxReferences As ImmutableArray(Of PortableExecutableReference) - - Friend Shared Function CreateInstance() As VisualBasicEditAndContinueTestHelpers - Return New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestMetadata.Net451.mscorlib, TestMetadata.Net451.System, TestMetadata.Net451.SystemCore)) - End Function - - Friend Shared Function CreateInstance40() As VisualBasicEditAndContinueTestHelpers - Return New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestMetadata.Net40.mscorlib, TestMetadata.Net40.SystemCore)) - End Function - - Friend Shared Function CreateInstanceMinAsync() As VisualBasicEditAndContinueTestHelpers - Return New VisualBasicEditAndContinueTestHelpers( - ImmutableArray.Create(TestReferences.NetFx.Minimal.mincorlib, TestReferences.NetFx.Minimal.minasync)) - End Function - - Public Sub New(fxReferences As ImmutableArray(Of PortableExecutableReference)) - _fxReferences = fxReferences + Public Sub New(Optional faultInjector As Action(Of SyntaxNode) = Nothing) + _analyzer = New VisualBasicEditAndContinueAnalyzer(faultInjector) End Sub Public Overrides ReadOnly Property Analyzer As AbstractEditAndContinueAnalyzer @@ -43,12 +27,17 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue End Get End Property - Public Overrides Function CreateLibraryCompilation(name As String, trees As IEnumerable(Of SyntaxTree)) As Compilation - Return VisualBasicCompilation.Create("New", - trees, - _fxReferences, - TestOptions.ReleaseDll.WithEmbedVbCoreRuntime(True)) - End Function + Public Overrides ReadOnly Property LanguageName As String + Get + Return LanguageNames.VisualBasic + End Get + End Property + + Public Overrides ReadOnly Property TopSyntaxComparer As TreeComparer(Of SyntaxNode) + Get + Return CodeAnalysis.VisualBasic.EditAndContinue.TopSyntaxComparer.Instance + End Get + End Property Public Overrides Function ParseText(source As String) As SyntaxTree Return SyntaxFactory.ParseSyntaxTree(source, VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest)) diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb index 70c364944d9ec..b97b5bc74b75d 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb @@ -8,6 +8,7 @@ Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests + Public Class LineEditTests Inherits EditingTestBase diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb index ac7c736e51c27..adcca4c678123 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/StatementEditingTests.vb @@ -10,7 +10,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests - + Public Class StatementEditingTests Inherits EditingTestBase @@ -506,7 +506,7 @@ Do : Dim a = 14, b = 15, c = 16 : Console.WriteLine(a + b + c) : Loop Dim src1 = "Dim result = From a In {Await F(0)}, b In {Q(Async Function() Await F(2))} Select a + b" & vbLf Dim src2 = "Dim result = From a In {Await F(1)}, b In {Q(Async Function() Await F(3))} Select a - b" & vbLf - Dim match = GetMethodMatch(src1, src2, stateMachine:=StateMachineKind.Async) + Dim match = GetMethodMatch(src1, src2, methodKind:=MethodKind.Async) Dim actual = ToMatchingPairs(match) ' Note that @@ -1261,7 +1261,7 @@ For Each x In {1, 2, 3} Yield 2 Next .Value - Dim match = GetMethodMatches(src1, src2, stateMachine:=StateMachineKind.Iterator) + Dim match = GetMethodMatches(src1, src2, stateMachine:=MethodKind.Iterator) Dim actual = ToMatchingPairs(match) Dim expected = New MatchingPairs From @@ -1300,7 +1300,7 @@ Catch e As Exception When filter(e) End Try " - Dim match = GetMethodMatches(src1, src2, stateMachine:=StateMachineKind.None) + Dim match = GetMethodMatches(src1, src2, stateMachine:=MethodKind.Regular) Dim actual = ToMatchingPairs(match) Dim expected = New MatchingPairs From @@ -4533,7 +4533,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics( ActiveStatementsDescription.Empty, - expectedSemanticEdits:= + semanticEdits:= { SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMembers("F").Single(), preserveLocalVariables:=True) }) @@ -5975,7 +5975,7 @@ Yield 2 Yield 3 Yield 4 .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Iterator) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Iterator) edits.VerifyEdits( "Update [Yield 1]@50 -> [Yield 3]@50", @@ -5993,7 +5993,7 @@ Yield 2 Yield 3 Yield 4 .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Iterator) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Iterator) edits.VerifyEdits( "Update [Yield 1]@50 -> [Yield 3]@50", @@ -6011,7 +6011,7 @@ Yield 1 Yield 2 Yield 3 .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Iterator) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Iterator) edits.VerifyEdits( "Insert [Yield 3]@66") @@ -6028,7 +6028,7 @@ Yield 3 Yield 1 Yield 2 .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Iterator) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Iterator) edits.VerifyEdits( "Delete [Yield 3]@66") @@ -6055,11 +6055,10 @@ Class C End Class " Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( - editScripts:={edits}, - activeStatements:=ActiveStatementsDescription.Empty, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}) + VerifySemantics( + edits, + diagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}, + targetFrameworks:={TargetFramework.Mscorlib40AndSystemCore}) End Sub @@ -6083,11 +6082,9 @@ Class C End Class " Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.CreateInstance40().VerifySemantics( - editScripts:={edits}, - activeStatements:=ActiveStatementsDescription.Empty, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:=Nothing) + VerifySemanticDiagnostics( + editScript:=edits, + targetFrameworks:={TargetFramework.Mscorlib40AndSystemCore}) End Sub #End Region @@ -6103,7 +6100,7 @@ Await F(2) Await F(3) Await F(4) .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Async) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Async) edits.VerifyEdits( "Update [Await F(1)]@40 -> [Await F(3)]@40", @@ -6121,7 +6118,7 @@ Await F(1) Await F(2) Await F(3) .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Async) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Async) edits.VerifyEdits( "Insert [Await F(2)]@51") @@ -6138,7 +6135,7 @@ Await F(3) Await F(1) Await F(3) .Value - Dim edits = GetMethodEdits(src1, src2, stateMachine:=StateMachineKind.Async) + Dim edits = GetMethodEdits(src1, src2, methodKind:=MethodKind.Async) edits.VerifyEdits( "Delete [Await F(2)]@51") @@ -6167,11 +6164,11 @@ Class C End Class " Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.CreateInstanceMinAsync().VerifySemantics( - editScripts:={edits}, - activeStatements:=ActiveStatementsDescription.Empty, - expectedSemanticEdits:=Nothing, - expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}) + + VerifySemantics( + edits, + diagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}, + targetFrameworks:={TargetFramework.MinimalAsync}) End Sub @@ -6196,7 +6193,9 @@ Class C End Class " Dim edits = GetTopEdits(src1, src2) - VisualBasicEditAndContinueTestHelpers.CreateInstanceMinAsync().VerifySemantics({edits}) + VerifySemanticDiagnostics( + edits, + targetFrameworks:={TargetFramework.MinimalAsync}) End Sub #End Region End Class diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index a0291eec5980f..2dc61abb6f17c 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -9,136 +9,229 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests - Public Class RudeEditTopLevelTests + + Public Class TopLevelEditingTests Inherits EditingTestBase #Region "Imports" Public Sub ImportDelete1() - Dim src1 As String = + Dim src1 = " Imports System.Diagnostics -.Value +" Dim src2 As String = "" + Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Delete [Imports System.Diagnostics]@1") + edits.VerifyEdits("Delete [Imports System.Diagnostics]@2") Assert.IsType(Of ImportsStatementSyntax)(edits.Edits.First().OldNode) Assert.Equal(edits.Edits.First().NewNode, Nothing) End Sub Public Sub ImportDelete2() - Dim src1 As String = -Imports System.Diagnostics + Dim src1 = " +Imports D = System.Diagnostics +Imports Imports System.Collections Imports System.Collections.Generic -.Value +" - Dim src2 As String = -Imports System.Diagnostics + Dim src2 = " Imports System.Collections.Generic -.Value +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Delete [Imports System.Collections]@28") + edits.VerifyEdits( + "Delete [Imports D = System.Diagnostics]@2", + "Delete [Imports ]@34", + "Delete [Imports System.Collections]@76") + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, Nothing, VBFeaturesResources.import), + Diagnostic(RudeEditKind.Delete, Nothing, VBFeaturesResources.import), Diagnostic(RudeEditKind.Delete, Nothing, VBFeaturesResources.import)) End Sub Public Sub ImportInsert() - Dim src1 As String = -Imports System.Diagnostics + Dim src1 = " Imports System.Collections.Generic -.Value +" - Dim src2 As String = -Imports System.Diagnostics + Dim src2 = " +Imports D = System.Diagnostics +Imports Imports System.Collections Imports System.Collections.Generic -.Value +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Insert [Imports System.Collections]@28") + edits.VerifyEdits( + "Insert [Imports D = System.Diagnostics]@2", + "Insert [Imports ]@34", + "Insert [Imports System.Collections]@76") + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "Imports D = System.Diagnostics", VBFeaturesResources.import), + Diagnostic(RudeEditKind.Insert, "Imports ", "import"), Diagnostic(RudeEditKind.Insert, "Imports System.Collections", VBFeaturesResources.import)) End Sub Public Sub ImportUpdate1() - Dim src1 As String = + Dim src1 = " Imports System.Diagnostics Imports System.Collections Imports System.Collections.Generic -.Value +" - Dim src2 As String = + Dim src2 = " Imports System.Diagnostics Imports X = System.Collections Imports System.Collections.Generic -.Value +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Update [Imports System.Collections]@28 -> [Imports X = System.Collections]@28") + edits.VerifyEdits( + "Update [Imports System.Collections]@30 -> [Imports X = System.Collections]@30") + edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Update, "Imports X = System.Collections", VBFeaturesResources.import)) End Sub - + Public Sub ImportUpdate2() - Dim src1 As String = + Dim src1 = " Imports System.Diagnostics Imports X1 = System.Collections +Imports Imports System.Collections.Generic -.Value +" - Dim src2 As String = + Dim src2 = " Imports System.Diagnostics Imports X2 = System.Collections +Imports Imports System.Collections.Generic -.Value +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Update [Imports X1 = System.Collections]@28 -> [Imports X2 = System.Collections]@28") + + ' TODO: https://github.com/dotnet/roslyn/issues/51374 + ' Should be following: + 'edits.VerifyEdits( + ' "Update [Imports X1 = System.Collections]@30 -> [Imports X2 = System.Collections]@30", + ' "Update [Imports ]@28 -> [Imports ]@28") + ' + 'edits.VerifyRudeDiagnostics( + ' Diagnostic(RudeEditKind.Update, "Imports X2 = System.Collections", VBFeaturesResources.import), + ' Diagnostic(RudeEditKind.Update, "Imports ", VBFeaturesResources.import)) + + edits.VerifyEdits( + "Update [Imports X1 = System.Collections]@30 -> [Imports X2 = System.Collections]@30") + edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Update, "Imports X2 = System.Collections", VBFeaturesResources.import)) End Sub Public Sub ImportUpdate3() - Dim src1 As String = + Dim src1 = " Imports System.Diagnostics Imports System.Collections Imports System.Collections.Generic -.Value +" - Dim src2 As String = + Dim src2 = " Imports System Imports System.Collections Imports System.Collections.Generic -.Value +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Update [Imports System.Diagnostics]@1 -> [Imports System]@1") + + edits.VerifyEdits( + "Update [Imports System.Diagnostics]@2 -> [Imports System]@2") + edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Update, "Imports System", VBFeaturesResources.import)) End Sub Public Sub ImportReorder1() - Dim src1 As String = + Dim src1 = " Imports System.Diagnostics Imports System.Collections Imports System.Collections.Generic -.Value +" - Dim src2 As String = + Dim src2 = " Imports System.Collections Imports System.Collections.Generic Imports System.Diagnostics -.Value +" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Reorder [Imports System.Diagnostics]@2 -> @66") + End Sub +#End Region + +#Region "Option" + + Public Sub OptionDelete() + Dim src1 = " +Option Strict On +" + + Dim src2 = " +" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Delete [Option Strict On]@2") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, Nothing, VBFeaturesResources.option_)) + End Sub + + + Public Sub OptionInsert() + Dim src1 = " +" + + Dim src2 = " +Option Strict On +" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Insert [Option Strict On]@2") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "Option Strict On", VBFeaturesResources.option_)) + End Sub + + + Public Sub OptionUpdate() + Dim src1 = " +Option Strict On +" + + Dim src2 = " +Option Strict Off +" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Reorder [Imports System.Diagnostics]@1 -> @63") + + edits.VerifyEdits( + "Update [Option Strict On]@2 -> [Option Strict Off]@2") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Update, "Option Strict Off", VBFeaturesResources.option_)) End Sub #End Region @@ -339,7 +432,10 @@ Imports System.Diagnostics Dim src2 = "Partial Interface C : End Interface" Dim edits = GetTopEdits(src1, src2) - edits.VerifyRudeDiagnostics() + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C")) + }) End Sub @@ -348,8 +444,8 @@ Imports System.Diagnostics Dim src2 = "" Dim edits = GetTopEdits(src1, src2) - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, Nothing, FeaturesResources.interface_)) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.interface_, "C"))) End Sub @@ -379,7 +475,7 @@ Imports System.Diagnostics Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, Nothing, VBFeaturesResources.module_)) + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(VBFeaturesResources.module_, "C"))) End Sub @@ -419,8 +515,7 @@ Imports System.Diagnostics edits.VerifyEdits( "Update [Module C]@0 -> [Partial Module C]@0") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Partial Module C", VBFeaturesResources.module_)) + edits.VerifyRudeDiagnostics() End Sub @@ -432,8 +527,7 @@ Imports System.Diagnostics edits.VerifyEdits( "Update [Partial Module C]@0 -> [Module C]@0") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Module C", VBFeaturesResources.module_)) + edits.VerifyRudeDiagnostics() End Sub @@ -471,8 +565,7 @@ Imports System.Diagnostics edits.VerifyEdits( "Update [Interface C]@0 -> [Partial Interface C]@0") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Partial Interface C", FeaturesResources.interface_)) + edits.VerifyRudeDiagnostics() End Sub @@ -640,361 +733,841 @@ End Interface Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics() End Sub -#End Region -#Region "Enums" - Public Sub Enum_NoModifiers_Insert() - Dim src1 = "" - Dim src2 = "Enum C : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Interface_InsertMembers() + Dim src1 = " +Imports System - edits.VerifyRudeDiagnostics() - End Sub +Interface I +End Interface +" + Dim src2 = " +Imports System - - Public Sub Enum_NoModifiers_IntoNamespace_Insert() - Dim src1 = "Namespace N : End Namespace" - Dim src2 = "Namespace N : Enum C : End Enum : End Namespace" - Dim edits = GetTopEdits(src1, src2) +Interface I + Sub VirtualMethod() + Property VirtualProperty() As String + Property VirtualIndexer(a As Integer) As String + Event VirtualEvent As Action - edits.VerifyRudeDiagnostics() - End Sub + MustInherit Class C + End Class - - Public Sub Enum_Name_Update() - Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Colors : Red = 1 : Blue = 2 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Interface J + End Interface - edits.VerifyEdits( - "Update [Enum Color]@0 -> [Enum Colors]@0") + Enum E + A + End Enum - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "Enum Colors", FeaturesResources.enum_)) + Delegate Sub D() +End Interface +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertVirtual, "Sub VirtualMethod()", FeaturesResources.method), + Diagnostic(RudeEditKind.InsertVirtual, "Property VirtualProperty()", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertVirtual, "Property VirtualIndexer(a As Integer)", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertVirtual, "Event VirtualEvent", FeaturesResources.event_)) End Sub - Public Sub Enum_Modifiers_Update() - Dim src1 = "Public Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Interface_InsertDelete() + Dim srcA1 = " +Interface I + Sub VirtualMethod() + Function VirtualFunction() As Integer + Property VirtualProperty() As String + ReadOnly Property VirtualReadonlyProperty() As String + Property VirtualIndexer(a As Integer) As String + Event VirtualEvent As Action - edits.VerifyEdits( - "Update [Public Enum Color]@0 -> [Enum Color]@0") + MustInherit Class C + End Class - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Enum Color", FeaturesResources.enum_)) - End Sub + Interface J + End Interface - - Public Sub Enum_BaseType_Insert() - Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Enum E + A + End Enum - edits.VerifyEdits( - "Insert [As UShort]@11") + Delegate Sub D() +End Interface +" + Dim srcB1 = " +" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As UShort", VBFeaturesResources.as_clause)) + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults() + }) End Sub - Public Sub Enum_BaseType_Update() - Dim src1 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color As Long : Red = 1 : Blue = 2 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub GenericType_InsertMembers() + Dim src1 = " +Imports System +Class C(Of T) +End Class +" + Dim src2 = " +Imports System +Class C(Of T) + Dim F1, F2 As New Object, F3 As Integer, F4 As New Object, F5(1, 2), F6? As Integer - edits.VerifyEdits( - "Update [As UShort]@11 -> [As Long]@11") + Sub M() + End Sub - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Enum Color", FeaturesResources.enum_)) - End Sub + Property P1(i As Integer) As Integer + Get + Return 1 + End Get + Set(value As Integer) + End Set + End Property - - Public Sub Enum_BaseType_Delete() - Dim src1 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Property P2 As Integer + Property P3 As New Object - edits.VerifyEdits( - "Delete [As UShort]@11") + Event E1(sender As Object, e As EventArgs) - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Enum Color", VBFeaturesResources.as_clause)) - End Sub + Event E2 As Action - - Public Sub Enum_Attribute_Insert() - Dim src1 = "Enum E : End Enum" - Dim src2 = "Enum E : End Enum" - Dim edits = GetTopEdits(src1, src2) + Custom Event E3 As EventHandler + AddHandler(value As EventHandler) + End AddHandler + RemoveHandler(value As EventHandler) + End RemoveHandler + RaiseEvent(sender As Object, e As EventArgs) + End RaiseEvent + End Event - edits.VerifyEdits( - "Insert []@0", "Insert [A]@1") + Dim WithEvents WE As Object - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) - End Sub + Enum N + A + End Enum - - Public Sub Enum_MemberAttribute_Delete() - Dim src1 = "Enum E : X : End Enum" - Dim src2 = "Enum E : X : End Enum" + Interface I + End Interface + + Class D + End Class + + Delegate Sub G() +End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits( - "Delete []@9", "Delete [A]@10") + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoGenericType, "Sub M()", FeaturesResources.method), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Property P1(i As Integer)", FeaturesResources.property_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Property P2", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Property P3", FeaturesResources.auto_property), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Event E1(sender As Object, e As EventArgs)", FeaturesResources.event_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Event E2", FeaturesResources.event_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "Event E3", FeaturesResources.event_), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F3 As Integer", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F4 As New Object", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F1", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F2", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F5(1, 2)", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "F6?", FeaturesResources.field), + Diagnostic(RudeEditKind.InsertIntoGenericType, "WE As Object", VBFeaturesResources.WithEvents_field)) + End Sub + + + Public Sub Type_Delete() + Dim src1 = " +Class C + Sub F() + End Sub +End Class - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "X", VBFeaturesResources.attributes)) - End Sub +Module M + Sub F() + End Sub +End Module - - Public Sub Enum_MemberAttribute_Insert() - Dim src1 = "Enum E : X : End Enum" - Dim src2 = "Enum E : X : End Enum" - Dim edits = GetTopEdits(src1, src2) +Structure S + Sub F() + End Sub +End Structure - edits.VerifyEdits( - "Insert []@9", "Insert [A]@10") +Interface I + Sub F() +End Interface - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) +Delegate Sub D() +" + Dim src2 = "" + + GetTopEdits(src1, src2).VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.class_, "C")), + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(VBFeaturesResources.module_, "M")), + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(VBFeaturesResources.structure_, "S")), + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.interface_, "I")), + Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.delegate_, "D"))) End Sub - Public Sub Enum_MemberAttribute_Update() - Dim src1 = "Enum E : X : End Enum" - Dim src2 = "Enum E : X : End Enum" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [A1]@10 -> [A2]@10") + Public Sub PartialType_Delete() + Dim srcA1 = " +Partial Class C + Sub F() + End Sub + Sub M() + End Sub +End Class" + Dim srcB1 = " +Partial Class C + Sub G() + End Sub +End Class +" + Dim srcA2 = "" + Dim srcB2 = " +Partial Class C + Sub G() + End Sub + Sub M() + End Sub +End Class" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(FeaturesResources.method, "C.F()"))}), + DocumentResults( + semanticEdits:={ + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("M")) + }) + }) End Sub - Public Sub Enum_MemberInitializer_Update1() - Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color : Red = 1 : Blue = 3 : End Enum" - - Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits( - "Update [Blue = 2]@23 -> [Blue = 3]@23") + Public Sub PartialType_InsertFirstDeclaration() + Dim src1 = "" + Dim src2 = " +Partial Class C + Sub F() + End Sub +End Class" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 3", FeaturesResources.enum_value)) + GetTopEdits(src1, src2).VerifySemantics( + ActiveStatementsDescription.Empty, + {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C"), preserveLocalVariables:=False)}) End Sub - Public Sub Enum_MemberInitializer_Update2() - Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" - Dim src2 = "Enum Color : Red = 1 << 0 : Blue = 2 << 1 : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub PartialType_InsertSecondDeclaration() + Dim srcA1 = " +Partial Class C + Sub F() + End Sub +End Class" + Dim srcB1 = "" - edits.VerifyEdits( - "Update [Red = 1]@13 -> [Red = 1 << 0]@13", - "Update [Blue = 2]@23 -> [Blue = 2 << 1]@28") + Dim srcA2 = " +Partial Class C + Sub F() + End Sub +End Class" + Dim srcB2 = " +Partial Class C + Sub G() + End Sub +End Class" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "Red = 1 << 0", FeaturesResources.enum_value), - Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 2 << 1", FeaturesResources.enum_value)) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("G"), preserveLocalVariables:=False) + }) + }) End Sub - Public Sub Enum_MemberInitializer_Update3() - Dim src1 = "Enum Color : Red = int.MinValue : End Enum" - Dim src2 = "Enum Color : Red = int.MaxValue : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Type_DeleteInsert() + Dim srcA1 = " +Class C + Sub F() + End Sub +End Class - edits.VerifyEdits( - "Update [Red = int.MinValue]@13 -> [Red = int.MaxValue]@13") +Module M + Sub F() + End Sub +End Module - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "Red = int.MaxValue", FeaturesResources.enum_value)) - End Sub +Structure S + Sub F() + End Sub +End Structure - - Public Sub Enum_MemberInitializer_Insert() - Dim src1 = "Enum Color : Red : End Enum" - Dim src2 = "Enum Color : Red = 1 : End Enum" - Dim edits = GetTopEdits(src1, src2) +Interface I + Sub F() +End Interface - edits.VerifyEdits( - "Update [Red]@13 -> [Red = 1]@13") +Delegate Sub D() +" + Dim srcB1 = "" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "Red = 1", FeaturesResources.enum_value)) + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("M").GetMember("F")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("S").GetMember("F")) + }) + }) End Sub - Public Sub Enum_MemberInitializer_Delete() - Dim src1 = "Enum Color : Red = 1 : End Enum" - Dim src2 = "Enum Color : Red : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub GenericType_DeleteInsert() + Dim srcA1 = " +Class C(Of T) + Sub F() + End Sub +End Class - edits.VerifyEdits( - "Update [Red = 1]@13 -> [Red]@13") +Structure S(Of T) + Sub F() + End Sub +End Structure - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.InitializerUpdate, "Red", FeaturesResources.enum_value)) +Interface I(Of T) + Sub F() +End Interface +" + Dim srcB1 = "" + + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:= + { + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "Sub F()", FeaturesResources.method), + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "Sub F()", FeaturesResources.method), + Diagnostic(RudeEditKind.GenericTypeTriviaUpdate, "Sub F()", FeaturesResources.method) + }) + }) End Sub - Public Sub Enum_Member_Insert1() - Dim src1 = "Enum Color : Red : End Enum" - Dim src2 = "Enum Color : Red : Blue : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Type_DeleteInsert_NonInsertableMembers() + Dim srcA1 = " +MustInherit Class C + Implements I + Public MustOverride Sub AbstractMethod() - edits.VerifyEdits( - "Insert [Blue]@19") + Public Overridable Sub VirtualMethod() + End Sub - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value)) - End Sub + Public Overrides Function ToString() As String + Return Nothing + End Function - - Public Sub Enum_Member_Insert2() - Dim src1 = "Enum Color : Red : End Enum" - Dim src2 = "Enum Color : Red : Blue : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub IG() Implements I.G + End Sub +End Class - edits.VerifyEdits( - "Insert [Blue]@19") +Interface I + Sub G() +End Interface +" + Dim srcB1 = "" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value)) + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("VirtualMethod")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("ToString")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("IG")) + }) + }) End Sub - Public Sub Enum_Member_Update() - Dim src1 = "Enum Color : Red : End Enum" - Dim src2 = "Enum Color : Orange : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Type_DeleteInsert_DataMembers() + Dim srcA1 = " +Class C + Dim F1 = 1, F2 As New Object, F3 As Integer, F4 As New Object, F5(1, 2), F6? As Integer +End Class +" + Dim srcB1 = "" - edits.VerifyEdits( - "Update [Red]@13 -> [Orange]@13") + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "Orange", FeaturesResources.enum_value)) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub Enum_Member_Delete() - Dim src1 = "Enum Color : Red : Blue : End Enum" - Dim src2 = "Enum Color : Red : End Enum" - Dim edits = GetTopEdits(src1, src2) + Public Sub Type_DeleteInsert_DataMembers_PartialSplit() + Dim srcA1 = " +Class C + Public x = 1 + Public y = 2 + Public z = 2 +End Class +" + Dim srcB1 = "" - edits.VerifyEdits( - "Delete [Blue]@19") + Dim srcA2 = " +Class C + Public x = 1 + Public y = 2 +End Class +" + Dim srcB2 = " +Partial Class C + Public z = 3 +End Class +" + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) + End sub - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Enum Color", FeaturesResources.enum_value)) - End Sub + + Public Sub Type_DeleteInsert_DataMembers_PartialMerge() + Dim srcA1 = " +Partial Class C + Public x = 1 + Public y = 2 +End Class +" + Dim srcB1 = " +Class C + Public z = 1 +End Class +" + + Dim srcA2 = " +Class C + Public x = 1 + Public y = 2 + Public z = 2 +End Class +" + + Dim 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, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }), + DocumentResults() + }) + End sub #End Region -#Region "Delegates" +#Region "Enums" - Public Sub Delegates_NoModifiers_Insert() + Public Sub Enum_NoModifiers_Insert() Dim src1 = "" - Dim src2 = "Delegate Sub C()" + Dim src2 = "Enum C : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics() End Sub - Public Sub Delegates_NoModifiers_IntoNamespace_Insert() + Public Sub Enum_NoModifiers_IntoNamespace_Insert() Dim src1 = "Namespace N : End Namespace" - Dim src2 = "Namespace N : Delegate Sub C() : End Namespace" + Dim src2 = "Namespace N : Enum C : End Enum : End Namespace" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics() End Sub - Public Sub Delegates_NoModifiers_IntoType_Insert() - Dim src1 = "Class N : End Class" - Dim src2 = "Class N : Delegate Sub C() : End Class" + Public Sub Enum_Name_Update() + Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Colors : Red = 1 : Blue = 2 : End Enum" Dim edits = GetTopEdits(src1, src2) - edits.VerifyRudeDiagnostics() + edits.VerifyEdits( + "Update [Enum Color]@0 -> [Enum Colors]@0") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Enum Colors", FeaturesResources.enum_)) End Sub - Public Sub Delegates_Rename() - Dim src1 = "Public Delegate Sub D()" - Dim src2 = "Public Delegate Sub Z()" + Public Sub Enum_Modifiers_Update() + Dim src1 = "Public Enum Color : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color : Red = 1 : Blue = 2 : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Public Delegate Sub D()]@0 -> [Public Delegate Sub Z()]@0") + "Update [Public Enum Color]@0 -> [Enum Color]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "Public Delegate Sub Z()", FeaturesResources.delegate_)) + Diagnostic(RudeEditKind.ModifiersUpdate, "Enum Color", FeaturesResources.enum_)) End Sub - Public Sub Delegates_Update_Modifiers() - Dim src1 = "Public Delegate Sub D()" - Dim src2 = "Private Delegate Sub D()" + Public Sub Enum_BaseType_Insert() + Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Public Delegate Sub D()]@0 -> [Private Delegate Sub D()]@0") + "Insert [As UShort]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.ModifiersUpdate, "Private Delegate Sub D()", FeaturesResources.delegate_)) + Diagnostic(RudeEditKind.Insert, "As UShort", VBFeaturesResources.as_clause)) End Sub - Public Sub Delegates_Update_ReturnType1() - Dim src1 = "Public Delegate Function D()" - Dim src2 = "Public Delegate Sub D()" + Public Sub Enum_BaseType_Update() + Dim src1 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color As Long : Red = 1 : Blue = 2 : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Public Delegate Function D()]@0 -> [Public Delegate Sub D()]@0") + "Update [As UShort]@11 -> [As Long]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_)) + Diagnostic(RudeEditKind.TypeUpdate, "Enum Color", FeaturesResources.enum_)) End Sub - Public Sub Delegates_Update_ReturnType2() - Dim src1 = "Public Delegate Function D() As Integer" - Dim src2 = "Public Delegate Sub D()" + Public Sub Enum_BaseType_Delete() + Dim src1 = "Enum Color As UShort : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color : Red = 1 : Blue = 2 : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Public Delegate Function D() As Integer]@0 -> [Public Delegate Sub D()]@0", - "Delete [As Integer]@29") + "Delete [As UShort]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_), - Diagnostic(RudeEditKind.Delete, "Public Delegate Sub D()", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.Delete, "Enum Color", VBFeaturesResources.as_clause)) End Sub - Public Sub Delegates_Update_ReturnType3() - Dim src1 = "Public Delegate Function D()" - Dim src2 = "Public Delegate Function D() As Integer" + Public Sub Enum_Attribute_Insert() + Dim src1 = "Enum E : End Enum" + Dim src2 = "Enum E : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Integer]@29") + "Insert []@0", "Insert [A]@1") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) End Sub - Public Sub Delegates_Update_ReturnType4() + Public Sub Enum_MemberAttribute_Delete() + Dim src1 = "Enum E : X : End Enum" + Dim src2 = "Enum E : X : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Delete []@9", "Delete [A]@10") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "X", VBFeaturesResources.attributes)) + End Sub + + + Public Sub Enum_MemberAttribute_Insert() + Dim src1 = "Enum E : X : End Enum" + Dim src2 = "Enum E : X : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Insert []@9", "Insert [A]@10") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + End Sub + + + Public Sub Enum_MemberAttribute_Update() + Dim src1 = "Enum E : X : End Enum" + Dim src2 = "Enum E : X : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [A1]@10 -> [A2]@10") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)) + End Sub + + + Public Sub Enum_MemberInitializer_Update1() + Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color : Red = 1 : Blue = 3 : End Enum" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyEdits( + "Update [Blue = 2]@23 -> [Blue = 3]@23") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 3", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_MemberInitializer_Update2() + Dim src1 = "Enum Color : Red = 1 : Blue = 2 : End Enum" + Dim src2 = "Enum Color : Red = 1 << 0 : Blue = 2 << 1 : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red = 1]@13 -> [Red = 1 << 0]@13", + "Update [Blue = 2]@23 -> [Blue = 2 << 1]@28") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "Red = 1 << 0", FeaturesResources.enum_value), + Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 2 << 1", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_MemberInitializer_Update3() + Dim src1 = "Enum Color : Red = int.MinValue : End Enum" + Dim src2 = "Enum Color : Red = int.MaxValue : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red = int.MinValue]@13 -> [Red = int.MaxValue]@13") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "Red = int.MaxValue", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_MemberInitializer_Insert() + Dim src1 = "Enum Color : Red : End Enum" + Dim src2 = "Enum Color : Red = 1 : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red]@13 -> [Red = 1]@13") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "Red = 1", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_MemberInitializer_Delete() + Dim src1 = "Enum Color : Red = 1 : End Enum" + Dim src2 = "Enum Color : Red : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red = 1]@13 -> [Red]@13") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.InitializerUpdate, "Red", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_Member_Insert1() + Dim src1 = "Enum Color : Red : End Enum" + Dim src2 = "Enum Color : Red : Blue : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Insert [Blue]@19") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_Member_Insert2() + Dim src1 = "Enum Color : Red : End Enum" + Dim src2 = "Enum Color : Red : Blue : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Insert [Blue]@19") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_Member_Update() + Dim src1 = "Enum Color : Red : End Enum" + Dim src2 = "Enum Color : Orange : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red]@13 -> [Orange]@13") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Orange", FeaturesResources.enum_value)) + End Sub + + + Public Sub Enum_Member_Delete() + Dim src1 = "Enum Color : Red : Blue : End Enum" + Dim src2 = "Enum Color : Red : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Delete [Blue]@19") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "Enum Color", DeletedSymbolDisplay(FeaturesResources.enum_value, "Blue"))) + End Sub +#End Region + +#Region "Delegates" + + Public Sub Delegates_NoModifiers_Insert() + Dim src1 = "" + Dim src2 = "Delegate Sub C()" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Delegates_NoModifiers_IntoNamespace_Insert() + Dim src1 = "Namespace N : End Namespace" + Dim src2 = "Namespace N : Delegate Sub C() : End Namespace" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Delegates_NoModifiers_IntoType_Insert() + Dim src1 = "Class N : End Class" + Dim src2 = "Class N : Delegate Sub C() : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Delegates_Rename() + Dim src1 = "Public Delegate Sub D()" + Dim src2 = "Public Delegate Sub Z()" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Public Delegate Sub D()]@0 -> [Public Delegate Sub Z()]@0") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Public Delegate Sub Z()", FeaturesResources.delegate_)) + End Sub + + + Public Sub Delegates_Update_Modifiers() + Dim src1 = "Public Delegate Sub D()" + Dim src2 = "Private Delegate Sub D()" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Public Delegate Sub D()]@0 -> [Private Delegate Sub D()]@0") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ModifiersUpdate, "Private Delegate Sub D()", FeaturesResources.delegate_)) + End Sub + + + Public Sub Delegates_Update_ReturnType1() + Dim src1 = "Public Delegate Function D()" + Dim src2 = "Public Delegate Sub D()" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Public Delegate Function D()]@0 -> [Public Delegate Sub D()]@0") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_)) + End Sub + + + Public Sub Delegates_Update_ReturnType2() + Dim src1 = "Public Delegate Function D() As Integer" + Dim src2 = "Public Delegate Sub D()" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Public Delegate Function D() As Integer]@0 -> [Public Delegate Sub D()]@0", + "Delete [As Integer]@29") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "Public Delegate Sub D()", FeaturesResources.delegate_), + Diagnostic(RudeEditKind.Delete, "Public Delegate Sub D()", VBFeaturesResources.as_clause)) + End Sub + + + Public Sub Delegates_Update_ReturnType3() + Dim src1 = "Public Delegate Function D()" + Dim src2 = "Public Delegate Function D() As Integer" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Insert [As Integer]@29") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) + End Sub + + + Public Sub Delegates_Update_ReturnType4() Dim src1 = "Public Delegate Function D() As Integer" Dim src2 = "Public Delegate Function D()" Dim edits = GetTopEdits(src1, src2) @@ -1330,110 +1903,114 @@ End Class Public Sub NestedClass_Insert_PInvoke_Syntactic() - Dim src1 As String = .Value +" - Dim src2 As String = .Value +" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Declare Ansi Function A Lib ""A"" ()", FeaturesResources.method), - Diagnostic(RudeEditKind.Insert, "Declare Ansi Sub B Lib ""B"" ()", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Function A Lib ""A"" ()", FeaturesResources.method), + Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Sub B Lib ""B"" ()", FeaturesResources.method)) End Sub Public Sub NestedClass_Insert_PInvoke_Semantic1() - Dim src1 As String = .Value +End Class" - Dim src2 As String = + Public Shared Function puts(c As String) As Integer End Function - + Public Shared Operator +(d As D, g As D) As Integer End Operator - + Public Shared Narrowing Operator CType(d As D) As Integer End Operator End Class -End Class -]]>.Value +End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.InsertDllImport, "puts"), - Diagnostic(RudeEditKind.InsertDllImport, "+"), - Diagnostic(RudeEditKind.InsertDllImport, "CType")) + edits.VerifySemantics( + diagnostics:= + { + Diagnostic(RudeEditKind.InsertDllImport, "Public Shared Function puts(c As String)", FeaturesResources.method), + Diagnostic(RudeEditKind.InsertDllImport, "Public Shared Operator +(d As D, g As D)", FeaturesResources.operator_), + Diagnostic(RudeEditKind.InsertDllImport, "Public Shared Narrowing Operator CType(d As D)", FeaturesResources.operator_) + }, + targetFrameworks:={TargetFramework.NetStandard20}) End Sub Public Sub NestedClass_Insert_PInvoke_Semantic2() - Dim src1 As String = .Value +End Class" - Dim src2 As String = + Private Shared Function puts(c As String) As Integer End Function -End Class -]]>.Value +End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.InsertDllImport, "puts")) + edits.VerifySemantics( + diagnostics:= + { + Diagnostic(RudeEditKind.InsertDllImport, "Private Shared Function puts(c As String)", FeaturesResources.method) + }, + targetFrameworks:={TargetFramework.NetStandard20}) End Sub Public Sub NestedClass_Insert_VirtualAbstract() - Dim src1 As String = + Dim src1 = " Imports System Imports System.Runtime.InteropServices Class C End Class -.Value +" - Dim src2 As String = + Dim src2 = " Imports System Imports System.Runtime.InteropServices @@ -1454,7 +2031,7 @@ Class C End Function End Class End Class -.Value +" Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics() End Sub @@ -1489,7 +2066,7 @@ End Class "Delete [()]@29") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Public Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()"))) End Sub @@ -1503,74 +2080,824 @@ End Class "Insert [Public Class D]@17", "Move [Public Class X : End Class]@17 -> @34") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "Public Class X", FeaturesResources.class_)) - End Sub + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Move, "Public Class X", FeaturesResources.class_)) + End Sub + + ''' + ''' A new generic type can be added whether it's nested and inherits generic parameters from the containing type, or top-level. + ''' + + public Sub NestedClassGeneric_Insert() + Dim src1 = " +Imports System +Class C(Of T) +End Class +" + Dim src2 = " +Imports System +Class C(Of T) + Class C + End Class + + Structure S + End Structure + + Enum N + A + End Enum + + Interface I + End Interface + + Class D + End Class + + Delegate Sub G() +End Class + +Class D(Of T) +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub NestedEnum_InsertMember() + Dim src1 = " +Structure S + Enum N + A = 1 + End Enum +End Structure +" + Dim src2 = " +Structure S + Enum N + A = 1 + B = 2 + End Enum +End Structure +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyEdits( + "Insert [B = 2]@40") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "B = 2", FeaturesResources.enum_value)) + End Sub + + + Public Sub NestedEnumInPartialType_InsertDelete() + Dim srcA1 = "Partial Structure S : End Structure" + Dim srcB1 = "Partial Structure S : Enum N : A = 1 : End Enum" + vbCrLf + "End Structure" + Dim srcA2 = "Partial Structure S : Enum N : A = 1 : End Enum" + vbCrLf + "End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults() + }) + End Sub + + + Public Sub NestedEnumInPartialType_InsertDeleteAndUpdateMember() + Dim srcA1 = "Partial Structure S : End Structure" + Dim srcB1 = "Partial Structure S : Enum N : A = 1 : End Enum" + vbCrLf + "End Structure" + Dim srcA2 = "Partial Structure S : Enum N : A = 2 : End Enum" + vbCrLf + "End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:= + { + Diagnostic(RudeEditKind.InitializerUpdate, "A = 2", FeaturesResources.enum_value) + }), + DocumentResults() + }) + End Sub + + + Public Sub NestedEnumInPartialType_InsertDeleteAndInsertMember() + Dim srcA1 = "Partial Structure S : End Structure" + Dim srcB1 = "Partial Structure S : Enum N : A = 1 : End Enum" + vbCrLf + "End Structure" + Dim srcA2 = "Partial Structure S : Enum N : A = 1 : B = 2 : End Enum" + vbCrLf + "End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Insert, "B = 2", FeaturesResources.enum_value)}), + DocumentResults() + }) + End Sub + + + public sub NestedDelegateInPartialType_InsertDelete() + Dim srcA1 = "Partial Structure S : End Structure" + Dim srcB1 = "Partial Structure S : Delegate Sub D()" + vbCrLf + "End Structure" + Dim srcA2 = "Partial Structure S : Delegate Sub D()" + vbCrLf + "End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + + ' delegate does not have any user-defined method body and this does not need a PDB update + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + ), + DocumentResults() + }) + End Sub + + + Public Sub NestedDelegateInPartialType_InsertDeleteAndChangeSignature() + Dim srcA1 = "Partial Structure S : End Structure" + Dim srcB1 = "Partial Structure S : Delegate Sub D()" + vbCrLf + "End Structure" + Dim srcA2 = "Partial Structure S : Delegate Sub D(a As Integer)" + vbCrLf + "End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:= + { + Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter) + }), + DocumentResults() + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteAndChange() + Dim srcA1 = "Partial Structure S : Partial Class C" + vbCrLf + "Sub F1() : End Sub : End Class : End Structure" + Dim srcB1 = "Partial Structure S : Partial Class C" + vbCrLf + "Sub F2(x As Byte) : End Sub : End Class : End Structure" + Dim srcC1 = "Partial Structure S : End Structure" + + Dim srcA2 = "Partial Structure S : Partial Class C" + vbCrLf + "Sub F1() : End Sub : End Class : End Structure" + Dim srcB2 = "Partial Structure S : End Structure" + Dim srcC2 = "Partial Structure S : Partial Class C" + vbCrLf + "Sub F2(x As Integer) : End Sub : End Class : End Structure" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Delete, "Partial Structure S", DeletedSymbolDisplay(FeaturesResources.method, "F2(Byte)"))}), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("S").GetMember(Of NamedTypeSymbol)("C").GetMember("F2"))}) + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteAndChange_BaseType() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "" + Dim srcC1 = "Partial Class C : End Class" + + Dim srcA2 = "" + Dim srcB2 = "Partial Class C : Inherits D" + vbCrLf + "End Class" + Dim srcC2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "Partial Class C", FeaturesResources.class_)}), + DocumentResults() + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "" + Dim srcC1 = "Partial Class C : End Class" + + Dim srcA2 = "" + Dim srcB2 = "Partial Class C : End Class" + Dim srcC2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}), + DocumentResults() + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteAndChange_Constraint() + Dim srcA1 = "Partial Class C(Of T) : End Class" + Dim srcB1 = "" + Dim srcC1 = "Partial Class C(Of T) : End Class" + + Dim srcA2 = "" + Dim srcB2 = "Partial Class C(Of T As New) : End Class" + Dim srcC2 = "Partial Class C(Of T) : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C(Of T As New)", FeaturesResources.class_)}), + DocumentResults() + }) + End Sub + + ''' + ''' Moves partial classes to different files while moving around their attributes and base interfaces. + ''' + + Public Sub NestedPartialTypeInPartialType_InsertDeleteRefactor() + Dim srcA1 = "Partial Class C : Implements I" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C : Implements J" + vbCrLf + "Sub G() : End Sub : End Class" + Dim srcC1 = "" + Dim srcD1 = "" + + Dim srcA2 = "" + Dim srcB2 = "" + Dim srcC2 = "Partial Class C : Implements I, J" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcD2 = "Partial Class C" + vbCrLf + "Sub G() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"))}), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("G"))}) + }) + End Sub + + ''' + ''' Moves partial classes to different files while moving around their attributes and base interfaces. + ''' Currently we do not support splitting attribute lists. + ''' + + Public Sub NestedPartialTypeInPartialType_InsertDeleteRefactor_AttributeListSplitting() + Dim srcA1 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Sub G() : End Sub : End Class" + Dim srcC1 = "" + Dim srcD1 = "" + + Dim srcA2 = "" + Dim srcB2 = "" + Dim srcC2 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcD2 = "Partial Class C" + vbCrLf + "Sub G() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults(diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}), + DocumentResults(diagnostics:={Diagnostic(RudeEditKind.Update, "Partial Class C", FeaturesResources.class_)}) + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteChangeMember() + Dim srcA1 = "Partial Class C" + vbCrLf + "Sub F(Optional y As Integer = 1) : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Sub G(Optional x As Integer = 1) : End Sub : End Class" + Dim srcC1 = "" + + Dim srcA2 = "" + Dim srcB2 = "Partial Class C" + vbCrLf + "Sub G(Optional x As Integer = 2) : End Sub : End Class" + Dim srcC2 = "Partial Class C" + vbCrLf + "Sub F(Optional y As Integer = 2) : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults(diagnostics:={Diagnostic(RudeEditKind.InitializerUpdate, "Optional x As Integer = 2", FeaturesResources.parameter)}), + DocumentResults(diagnostics:={Diagnostic(RudeEditKind.InitializerUpdate, "Optional y As Integer = 2", FeaturesResources.parameter)}) + }) + End Sub + + + Public Sub NestedPartialTypeInPartialType_InsertDeleteAndInsertVirtual() + Dim srcA1 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub F1()" + vbCrLf + "End Sub : End Class : End Interface" + Dim srcB1 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub F2()" + vbCrLf + "End Sub : End Class : End Interface" + Dim srcC1 = "Partial Interface I : Partial Class C" + vbCrLf + "End Class : End Interface" + Dim srcD1 = "Partial Interface I : Partial Class C" + vbCrLf + "End Class : End Interface" + Dim srcE1 = "Partial Interface I : End Interface" + Dim srcF1 = "Partial Interface I : End Interface" + + Dim srcA2 = "Partial Interface I : Partial Class C" + vbCrLf + "End Class : End Interface" + Dim srcB2 = "" + Dim srcC2 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub F1()" + vbCrLf + "End Sub : End Class : End Interface" ' move existing virtual into existing partial decl + Dim srcD2 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub N1()" + vbCrLf + "End Sub : End Class : End Interface" ' insert new virtual into existing partial decl + Dim srcE2 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub F2()" + vbCrLf + "End Sub : End Class : End Interface" ' move existing virtual into a new partial decl + Dim srcF2 = "Partial Interface I : Partial Class C" + vbCrLf + "Overridable Sub N2()" + vbCrLf + "End Sub : End Class : End Interface" ' 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)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("I").GetMember(Of NamedTypeSymbol)("C").GetMember("F1"))}), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.InsertVirtual, "Overridable Sub N1()", FeaturesResources.method)}), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("I").GetMember(Of NamedTypeSymbol)("C").GetMember("F2"))}), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.InsertVirtual, "Overridable Sub N2()", FeaturesResources.method)}) + }) + End Sub + +#End Region + +#Region "Namespaces" + + Public Sub NamespaceInsert() + Dim src1 = "" + Dim src2 = "Namespace C : End Namespace" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "Namespace C", FeaturesResources.namespace_)) + End Sub + + + Public Sub NamespaceDelete() + Dim src1 = "Namespace C : End Namespace" + Dim src2 = "" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, Nothing, FeaturesResources.namespace_)) + End Sub + + + Public Sub NamespaceMove1() + Dim src1 = "Namespace C : Namespace D : End Namespace : End Namespace" + Dim src2 = "Namespace C : End Namespace : Namespace D : End Namespace" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Move [Namespace D : End Namespace]@14 -> @30") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Move, "Namespace D", FeaturesResources.namespace_)) + End Sub + + + Public Sub NamespaceReorder1() + Dim src1 = "Namespace C : Namespace D : End Namespace : Class T : End Class : Namespace E : End Namespace : End Namespace" + Dim src2 = "Namespace C : Namespace E : End Namespace : Class T : End Class : Namespace D : End Namespace : End Namespace" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Reorder [Class T : End Class]@44 -> @44", + "Reorder [Namespace E : End Namespace]@66 -> @14") + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub NamespaceReorder2() + Dim src1 = "Namespace C : " & + "Namespace D1 : End Namespace : " & + "Namespace D2 : End Namespace : " & + "Namespace D3 : End Namespace : " & + "Class T : End Class : " & + "Namespace E : End Namespace : " & + "End Namespace" + + Dim src2 = "Namespace C : " & + "Namespace E : End Namespace : " & + "Class T : End Class : " & + "Namespace D1 : End Namespace : " & + "Namespace D2 : End Namespace : " & + "Namespace D3 : End Namespace : " & + "End Namespace" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Reorder [Class T : End Class]@107 -> @44", + "Reorder [Namespace E : End Namespace]@129 -> @14") + + edits.VerifyRudeDiagnostics() + End Sub + +#End Region + +#Region "Members" + + + Public Sub PartialMember_DeleteInsert() + Dim srcA1 = " +Imports System + +Partial Class C + Dim F1 = 1, F2 As New Object, F3 As Integer, F4 As New Object, F5(1, 2), F6? As Integer + + Sub M() + End Sub + + Property P1(i As Integer) As Integer + Get + Return 1 + End Get + Set(value As Integer) + End Set + End Property + + Property P2 As Integer + Property P3 As New Object + + Event E1(sender As Object, e As EventArgs) + + Event E2 As Action + + Custom Event E3 As EventHandler + AddHandler(value As EventHandler) + End AddHandler + RemoveHandler(value As EventHandler) + End RemoveHandler + RaiseEvent(sender As Object, e As EventArgs) + End RaiseEvent + End Event + + Dim WithEvents WE As Object +End Class +" + Dim srcB1 = " +Imports System + +Partial Class C +End Class +" + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("M")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P1").GetMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P1").SetMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E3").AddMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E3").RemoveMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E3").RaiseMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) + End Sub + + + Public Sub PartialMember_InsertDelete_MultipleDocuments() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcA2 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"), preserveLocalVariables:=False) + }), + DocumentResults() + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_MultipleDocuments() + Dim srcA1 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C : End Class" + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F"), preserveLocalVariables:=False) + }) + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_GenericMethod() + Dim srcA1 = "Partial Class C" + vbCrLf + "Sub F(Of T)() : End Sub : End Class" + Dim srcB1 = "Partial Class C : End Class" + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Sub F(Of T)() : End Sub : End Class" + + ' TODO better message + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "Sub F(Of T)()", FeaturesResources.method) + }) + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_GenericType() + Dim srcA1 = "Partial Class C(Of T)" + vbCrLf + "Sub F(Of T)() : End Sub : End Class" + Dim srcB1 = "Partial Class C(Of T) : End Class" + Dim srcA2 = "Partial Class C(Of T) : End Class" + Dim srcB2 = "Partial Class C(Of T)" + vbCrLf + "Sub F(Of T)() : End Sub : End Class" + + ' TODO better message + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "Sub F(Of T)()", FeaturesResources.method) + }) + }) + End Sub + + + Public Sub PartialNestedType_InsertDeleteAndChange() + Dim srcA1 = " +Partial Class C +End Class +" + Dim srcB1 = " +Partial Class C + Class D + Sub M() + End Sub + End Class + + Interface I + End Interface +End Class" + + Dim srcA2 = " +Partial Class C + Class D + Implements I + + Sub M() + End Sub + End Class + + Interface I + End Interface +End Class" + + Dim srcB2 = " +Partial Class C +End Class +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + diagnostics:= + { + Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "Class D", FeaturesResources.class_) + }), + DocumentResults() + }) + End Sub + + + Public Sub PartialMember_RenameInsertDelete() + ' The syntactic analysis for A And B produce rename edits since it doesn't see that the member was in fact moved. + ' TODO: Currently, we don't even pass rename edits to semantic analysis where we could handle them as updates. + + Dim srcA1 = "Partial Class C" + vbCrLf + "Sub F1() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Sub F2() : End Sub : End Class" + Dim srcA2 = "Partial Class C" + vbCrLf + "Sub F2() : End Sub : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Sub F1() : End Sub : End Class" + + ' current outcome + GetTopEdits(srcA1, srcA2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Renamed, "Sub F2()", FeaturesResources.method)) + GetTopEdits(srcB1, srcB2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.Renamed, "Sub F1()", FeaturesResources.method)) + + ' correct outcome + 'EditAndContinueValidation.VerifySemantics( + ' { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + ' + ' { + ' DocumentResults(semanticEdits:= + ' { + ' SemanticEdit(SemanticEditKind.Update, c => c.GetMember(Of NamedTypeSymbol)("C").GetMember("F2")), + ' }), + ' + ' DocumentResults( + ' semanticEdits:= + ' { + ' SemanticEdit(SemanticEditKind.Update, c => c.GetMember(Of NamedTypeSymbol)("C").GetMember("F1")), + ' }) + ' }); + End Sub + + + Public Sub PartialMember_DeleteInsert_UpdateMethodBodyError() + Dim srcA1 = " +Imports System.Collections.Generic + +Partial Class C + Iterator Function F() As IEnumerable(Of Integer) + Yield 1 + End Function +End Class +" + Dim srcB1 = " +Imports System.Collections.Generic + +Partial Class C +End Class +" + + Dim srcA2 = " +Imports System.Collections.Generic + +Partial Class C +End Class +" + Dim srcB2 = " +Imports System.Collections.Generic + +Partial Class C + Iterator Function F() As IEnumerable(Of Integer) + Yield 1 + Yield 2 + End Function +End Class +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.Insert, "Yield 2", VBFeaturesResources.Yield_statement) + }) + }) + End Sub + + + Public Sub PartialMember_DeleteInsert_UpdatePropertyAccessors() + Dim srcA1 = " +Partial Class C + Property P As Integer + Get + Return 1 + End Get + Set(value As Integer) + Console.WriteLine(1) + End Set + End Property +End Class +" + Dim srcB1 = "Partial Class C: End Class" + + Dim srcA2 = "Partial Class C: End Class" + Dim srcB2 = " +Partial Class C + Property P As Integer + Get + Return 2 + End Get + Set(value As Integer) + Console.WriteLine(2) + End Set + End Property +End Class" -#End Region + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P").GetMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P").SetMethod) + }) + }) + End Sub -#Region "Namespaces" - Public Sub NamespaceInsert() - Dim src1 = "" - Dim src2 = "Namespace C : End Namespace" - Dim edits = GetTopEdits(src1, src2) + Public Sub PartialMember_DeleteInsert_UpdateAutoProperty() + Dim srcA1 = "Partial Class C" + vbCrLf + "Property P As Integer = 1 : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Namespace C", FeaturesResources.namespace_)) + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Property P As Integer = 2 : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub NamespaceMove1() - Dim src1 = "Namespace C : Namespace D : End Namespace : End Namespace" - Dim src2 = "Namespace C : End Namespace : Namespace D : End Namespace" - Dim edits = GetTopEdits(src1, src2) + Public Sub PartialMember_DeleteInsert_AddFieldInitializer() + Dim srcA1 = "Partial Class C" + vbCrLf + "Dim P As Integer : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyEdits( - "Move [Namespace D : End Namespace]@14 -> @30") + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Dim P As Integer = 1 : End Class" - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "Namespace D", FeaturesResources.namespace_)) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub NamespaceReorder1() - Dim src1 = "Namespace C : Namespace D : End Namespace : Class T : End Class : Namespace E : End Namespace : End Namespace" - Dim src2 = "Namespace C : Namespace E : End Namespace : Class T : End Class : Namespace D : End Namespace : End Namespace" - Dim edits = GetTopEdits(src1, src2) + Public Sub PartialMember_DeleteInsert_RemoveFieldInitializer() + Dim srcA1 = "Partial Class C" + vbCrLf + "Dim P As Integer = 1 : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyEdits( - "Reorder [Class T : End Class]@44 -> @44", - "Reorder [Namespace E : End Namespace]@66 -> @14") + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Dim P As Integer : End Class" - edits.VerifyRudeDiagnostics() + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub NamespaceReorder2() - Dim src1 = "Namespace C : " & - "Namespace D1 : End Namespace : " & - "Namespace D2 : End Namespace : " & - "Namespace D3 : End Namespace : " & - "Class T : End Class : " & - "Namespace E : End Namespace : " & - "End Namespace" - - Dim src2 = "Namespace C : " & - "Namespace E : End Namespace : " & - "Class T : End Class : " & - "Namespace D1 : End Namespace : " & - "Namespace D2 : End Namespace : " & - "Namespace D3 : End Namespace : " & - "End Namespace" + Public Sub PartialMember_DeleteInsert_ConstructorWithInitializers() + Dim srcA1 = " +Partial Class C + Dim F = 1 - Dim edits = GetTopEdits(src1, src2) + Sub New(x As Integer) + F = x + End Sub +End Class" + Dim srcB1 = " +Partial Class C +End Class" - edits.VerifyEdits( - "Reorder [Class T : End Class]@107 -> @44", - "Reorder [Namespace E : End Namespace]@129 -> @14") + Dim srcA2 = " +Partial Class C + Dim F = 1 +End Class" + Dim srcB2 = " +Partial Class C + Sub New(x As Integer) + F = x + 1 + End Sub +End Class" - edits.VerifyRudeDiagnostics() + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub #End Region @@ -1661,7 +2988,7 @@ End Class "Delete [()]@15") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()"))) End Sub @@ -1672,7 +2999,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Interface C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Interface C", DeletedSymbolDisplay(FeaturesResources.method, "Goo()"))) End Sub @@ -1691,7 +3018,7 @@ End Class "Delete [As Integer]@18") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(Integer)"))) End Sub @@ -1712,7 +3039,7 @@ End Class "Delete [As Integer]@32") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "goo(Integer)"))) End Sub @@ -1800,7 +3127,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "MustOverride Sub F", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertVirtual, "MustOverride Sub F", FeaturesResources.method)) End Sub @@ -1814,6 +3141,36 @@ End Class Diagnostic(RudeEditKind.InsertVirtual, "Overrides Sub F", FeaturesResources.method)) End Sub + + Public Sub ExternMethodDeleteInsert() + Dim srcA1 = " +Imports System +Imports System.Runtime.InteropServices + +Class C + + Public Shared Function puts(c As String) As Integer + End Function +End Class" + Dim srcA2 = " +Imports System +Imports System.Runtime.InteropServices +" + + Dim srcB1 = srcA2 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("puts")) + }) + }) + End Sub + Public Sub Method_Reorder1() Dim src1 = "Class C : " & vbLf & "Sub f(a As Integer, b As Integer)" & vbLf & "a = b : End Sub : " & vbLf & "Sub g() : End Sub : End Class" @@ -1866,7 +3223,7 @@ End Class "Delete [As Integer]@55") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "f(Integer, Integer)"))) End Sub @@ -2442,14 +3799,14 @@ End Class Public Sub MethodInsert_Handles_Clause() - Dim src1 = "Class C : Event E1 As Action" & vbLf & "End Class" - Dim src2 = "Class C : Event E1 As Action" & vbLf & "Private Sub Goo() Handles Me.E1 : End Sub : End Class" + Dim src1 = "Class C : Event E1 As System.Action" & vbLf & "End Class" + Dim src2 = "Class C : Event E1 As System.Action" & vbLf & "Private Sub Goo() Handles Me.E1 : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [Private Sub Goo() Handles Me.E1 : End Sub]@29", - "Insert [Private Sub Goo() Handles Me.E1]@29", - "Insert [()]@44") + "Insert [Private Sub Goo() Handles Me.E1 : End Sub]@36", + "Insert [Private Sub Goo() Handles Me.E1]@36", + "Insert [()]@51") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.InsertHandlesClause, "Private Sub Goo()", FeaturesResources.method)) @@ -2483,6 +3840,295 @@ End Class edits.VerifyRudeDiagnostics() End Sub + + + Public Sub PartialMethod_DeleteInsert_DefinitionPart() + Dim srcA1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcC1 = "Partial Class C : End Class" + + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "partial class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcC2 = "partial class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F").PartialImplementationPart)}) + }) + End Sub + + + Public Sub PartialMethod_DeleteInsert_ImplementationPart() + Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + Dim srcC1 = "Partial Class C : End Class" + + Dim srcA2 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C : End Class" + Dim srcC2 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F").PartialImplementationPart)}) + }) + End Sub + + + Public Sub PartialMethod_Swap_ImplementationAndDefinitionParts() + Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + Dim srcA2 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + + ' TODO: current + GetTopEdits(srcA1, srcA2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.ModifiersUpdate, "Private Sub F()", FeaturesResources.method)) + GetTopEdits(srcB1, srcB2).VerifyRudeDiagnostics(Diagnostic(RudeEditKind.ModifiersUpdate, "Partial Private Sub F()", FeaturesResources.method)) + + ' TODO: correct + ' EditAndContinueValidation.VerifySemantics( + ' { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + ' + ' { + ' DocumentResults(), + ' DocumentResults( + ' semanticEdits:= { SemanticEdit(SemanticEditKind.Update, c => c.GetMember(Of NamedTypeSymbol)("C").GetMember("F")) }), + ' }); + End Sub + + + Public Sub PartialMethod_DeleteImplementation() + Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + Dim srcA2 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Delete, "Partial Class C", DeletedSymbolDisplay(FeaturesResources.method, "F()"))}) + }) + End Sub + + + Public Sub PartialMethod_DeleteBoth() + Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.Delete, "Partial Class C", DeletedSymbolDisplay(FeaturesResources.method, "F()"))}) + }) + End Sub + + + Public Sub PartialMethod_DeleteInsertBoth() + Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + Dim srcC1 = "Partial Class C : End Class" + Dim srcD1 = "Partial Class C : End Class" + + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : End Class" + Dim srcC2 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcD2 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)}, + { + DocumentResults(), + DocumentResults(), + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F").PartialImplementationPart)}) + }) + End Sub + + + Public Sub PartialMethod_Insert() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "Partial Class C : End Class" + + Dim srcA2 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F").PartialImplementationPart)}) + }) + End Sub +#End Region + +#Region "Operators" + + + Public Sub OperatorInsert() + Dim src1 = " +Class C +End Class +" + Dim src2 = " +Class C + Public Shared Operator +(d As C, g As C) As Integer + Return Nothing + End Operator + + Public Shared Narrowing Operator CType(d As C) As Integer + Return Nothing + End Operator +End Class +" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertOperator, "Public Shared Operator +(d As C, g As C)", FeaturesResources.operator_), + Diagnostic(RudeEditKind.InsertOperator, "Public Shared Narrowing Operator CType(d As C)", FeaturesResources.operator_)) + End Sub + + + Public Sub OperatorDelete() + Dim src1 = " +Class C + Public Shared Operator +(d As C, g As C) As Integer + Return Nothing + End Operator + + Public Shared Narrowing Operator CType(d As C) As Integer + Return Nothing + End Operator +End Class +" + Dim src2 = " +Class C +End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "+(C, C)")), + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.operator_, "CType(C)"))) + End Sub + + + Public Sub OperatorInsertDelete() + Dim srcA1 = " +Partial Class C + Public Shared Narrowing Operator CType(d As C) As Integer + Return Nothing + End Operator +End Class +" + Dim srcB1 = " +Partial Class C + Public Shared Operator +(d As C, g As C) As Integer + Return Nothing + End Operator +End Class +" + + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("op_Addition")) + }), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("op_Implicit")) + }) + }) + End Sub + + + Public Sub OperatorUpdate() + Dim src1 = " +Class C + Public Shared Narrowing Operator CType(d As C) As Integer + Return 0 + End Operator + + Public Shared Operator +(d As C, g As C) As Integer + Return 0 + End Operator +End Class +" + Dim src2 = " +Class C + Public Shared Narrowing Operator CType(d As C) As Integer + Return 1 + End Operator + + Public Shared Operator +(d As C, g As C) As Integer + Return 1 + End Operator +End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics(ActiveStatementsDescription.Empty, + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("op_Explicit")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("op_Addition")) + }) + End Sub + + + Public Sub OperatorReorder() + Dim src1 = " +Class C + Public Shared Narrowing Operator CType(d As C) As Integer + Return 0 + End Operator + + Public Shared Operator +(d As C, g As C) As Integer + Return 0 + End Operator +End Class +" + Dim src2 = " +Class C + Public Shared Operator +(d As C, g As C) As Integer + Return 0 + End Operator + + Public Shared Narrowing Operator CType(d As C) As Integer + Return 0 + End Operator +End Class +" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Reorder [Public Shared Operator +(d As C, g As C) As Integer + Return 0 + End Operator]@116 -> @15") + + edits.VerifyRudeDiagnostics() + End Sub #End Region #Region "Constructors" @@ -2555,7 +4201,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(VBFeaturesResources.Shared_constructor, "New()"))) End Sub @@ -2565,7 +4211,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Module C", FeaturesResources.constructor)) + Diagnostic(RudeEditKind.Delete, "Module C", DeletedSymbolDisplay(FeaturesResources.constructor, "New()"))) End Sub @@ -2575,7 +4221,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -2585,47 +4231,39 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single())}) - End Sub - - - Public Sub InstanceCtorDelete_Private1() - Dim src1 = "Class C" & vbLf & "Private Sub New() : End Sub : End Class" - Dim src2 = "Class C : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub - - Public Sub InstanceCtorDelete_Protected() - Dim src1 = "Class C" & vbLf & "Protected Sub New() : End Sub : End Class" + + + + + + Public Sub InstanceCtorDelete_NonPublic(visibility As String) + Dim src1 = "Class C" & vbLf & visibility & " Sub New() : End Sub : End Class" Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + Diagnostic(RudeEditKind.ChangingVisibility, "Class C", DeletedSymbolDisplay(FeaturesResources.constructor, "New()"))) End Sub - Public Sub InstanceCtorDelete_Internal() - Dim src1 = "Class C" & vbLf & "Friend Sub New() : End Sub : End Class" - Dim src2 = "Class C : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) - End Sub + Public Sub InstanceCtorDelete_Public_PartialWithInitializerUpdate() + Dim srcA1 = "Class C" & vbLf & "Public Sub New() : End Sub : End Class" + Dim srcB1 = "Class C" & vbLf & "Dim x = 1 : End Class" - - Public Sub InstanceCtorDelete_ProtectedInternal() - Dim src1 = "Class C" & vbLf & "Protected Friend Sub New() : End Sub : End Class" - Dim src2 = "Class C : End Class" - Dim edits = GetTopEdits(src1, src2) + Dim srcA2 = "Class C : End Class" + Dim srcB2 = "Class C" & vbLf & "Dim x = 2 : End Class" - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True)}), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True)}) + }) End Sub @@ -2666,11 +4304,13 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "End Class" Dim srcB2 = "Partial Class C" & vbLf & "Sub New() : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' no change in document A + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:= { - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) }) End Sub @@ -2682,7 +4322,7 @@ End Class edits.VerifySemantics( ActiveStatementsDescription.Empty, - expectedSemanticEdits:= + semanticEdits:= { SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(Function(m) m.Parameters.IsEmpty)) }) @@ -2696,52 +4336,28 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C" & vbLf & "Sub New(a As Integer) : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' no change in document B + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:= { - SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(Function(m) m.Parameters.IsEmpty)) + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(Function(m) m.Parameters.IsEmpty))}), + DocumentResults() }) End Sub - - Public Sub InstanceCtorInsert_Private_Implicit1() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C" & vbLf & "Private Sub New() : End Sub : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Private Sub New()")) - End Sub - - - Public Sub InstanceCtorInsert_Protected_PublicImplicit() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C" & vbLf & "Protected Sub New() : End Sub : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Protected Sub New()")) - End Sub - - - Public Sub InstanceCtorInsert_Internal_PublicImplicit() + + + + + + Public Sub InstanceCtorInsert_Private_Implicit1(visibility As String) Dim src1 = "Class C : End Class" - Dim src2 = "Class C" & vbLf & "Friend Sub New() : End Sub : End Class" + Dim src2 = "Class C" & vbLf & visibility & " Sub New() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Friend Sub New()")) - End Sub - - - Public Sub InstanceCtorInsert_Internal_ProtectedImplicit() - Dim src1 = "MustInherit Class C : End Class" - Dim src2 = "MustInherit Class C" & vbLf & "Friend Sub New() : End Sub : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Friend Sub New()")) + Diagnostic(RudeEditKind.ChangingVisibility, visibility & " Sub New()", FeaturesResources.constructor)) End Sub @@ -2765,28 +4381,13 @@ End Class InstanceConstructors.Single(Function(ctor) ctor.DeclaredAccessibility = Accessibility.Private))}) End Sub - - Public Sub InstanceCtorInsert_Internal_NoImplicit() - Dim src1 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : End Class" - Dim src2 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : " & vbLf & "Friend Sub New() : End Sub : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics() - End Sub - - - Public Sub InstanceCtorInsert_Protected_NoImplicit() - Dim src1 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : End Class" - Dim src2 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : " & vbLf & "Protected Sub New() : End Sub : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifySemanticDiagnostics() - End Sub - - - Public Sub InstanceCtorInsert_FriendProtected_NoImplicit() + + + + + Public Sub InstanceCtorInsert_Internal_NoImplicit(visibility As String) Dim src1 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : End Class" - Dim src2 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : " & vbLf & "Friend Protected Sub New() : End Sub : End Class" + Dim src2 = "Class C" & vbLf & "Public Sub New(a As Integer) : End Sub : " & vbLf & visibility & " Sub New() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics() @@ -2800,22 +4401,33 @@ End Class Dim srcA2 = "Partial Class C : End Class" Dim srcB2 = "Partial Class C" & vbLf & "Shared Sub New() : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + }) End Sub - Public Sub ModuleCtor_Partial_Delete() + Public Sub ModuleCtor_Partial_DeleteInsert() Dim srcA1 = "Partial Module C" & vbLf & "Sub New() : End Sub : End Module" Dim srcB1 = "Partial Module C : End Module" Dim srcA2 = "Partial Module C : End Module" Dim srcB2 = "Partial Module C" & vbLf & "Sub New() : End Sub : End Module" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + ), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + }) End Sub @@ -2826,9 +4438,15 @@ End Class Dim srcA2 = "Partial Class C : End Class" Dim srcB2 = "Partial Class C" & vbLf & "Private Sub New() : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + ), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + }) End Sub @@ -2839,9 +4457,14 @@ End Class Dim srcA2 = "Partial Class C : End Class" Dim srcB2 = "Partial Class C" & vbLf & "Public Sub New() : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + }) End Sub @@ -2852,9 +4475,14 @@ End Class Dim srcA2 = "Partial Class C : End Class" Dim srcB2 = "Partial Class C" & vbLf & "Public Sub New() : End Sub : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be reported as rude edit in the other document where it was inserted back with changed visibility + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedDiagnostics:={Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Public Sub New()")}) + { + DocumentResults(), + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.ModifiersUpdate, "Public Sub New()", FeaturesResources.constructor)}) + }) End Sub @@ -2865,9 +4493,15 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Shared Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be reported as rude edit in the other document where it was inserted back with changed visibility + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults( + ) + }) End Sub @@ -2878,9 +4512,15 @@ End Class Dim srcA2 = "Partial Module C" & vbLf & "Sub New() : End Sub : End Module" Dim srcB2 = "Partial Module C : End Module" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults( + ) + }) End Sub @@ -2891,9 +4531,15 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults( + ) + }) End Sub @@ -2904,9 +4550,15 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Private Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults( + ) + }) End Sub @@ -2917,9 +4569,15 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Friend Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults( + ) + }) End Sub @@ -2930,35 +4588,39 @@ End Class Dim srcA2 = "Partial Class C" & vbLf & "Friend Sub New()" & vbLf & "Console.WriteLine(1) : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedSemanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults() + }) End Sub - - Public Sub InstanceCtor_Partial_InsertPublicDeletePrivate() - Dim srcA1 = "Partial Class C : End Class" - Dim srcB1 = "Partial Class C" & vbLf & "Private Sub New() : End Sub : End Class" - - Dim srcA2 = "Partial Class C" & vbLf & "Sub New() : End Sub : End Class" - Dim srcB2 = "Partial Class C : End Class" - - EditAndContinueValidation.VerifySemantics( - {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedDiagnostics:={Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Sub New()")}) - End Sub + + + + + Public Sub InstanceCtor_Partial_VisibilityUpdate(visibility As String) + If visibility.Length > 0 Then + visibility &= " " + End If - - Public Sub InstanceCtor_Partial_InsertInternalDeletePrivate() Dim srcA1 = "Partial Class C : End Class" Dim srcB1 = "Partial Class C" & vbLf & "Private Sub New() : End Sub : End Class" - Dim srcA2 = "Partial Class C" & vbLf & "Friend Sub New() : End Sub : End Class" + Dim srcA2 = "Partial Class C" & vbLf & visibility & "Sub New() : End Sub : End Class" Dim srcB2 = "Partial Class C : End Class" - EditAndContinueValidation.VerifySemantics( + ' delete of the constructor in partial part will be reported as rude edit in the other document where it was inserted back with changed visibility + VerifySemantics( {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, - expectedDiagnostics:={Diagnostic(RudeEditKind.ChangingConstructorVisibility, "Friend Sub New()")}) + { + DocumentResults( + diagnostics:={Diagnostic(RudeEditKind.ModifiersUpdate, visibility & "Sub New()", FeaturesResources.constructor)}), + DocumentResults() + }) End Sub @@ -3175,9 +4837,8 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim syntaxMap = GetSyntaxMap(src1, src2) - edits.VerifySemantics( - ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(), syntaxMap(0))}) + edits.VerifySemanticDiagnostics( + {Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "Sub New()")}) End Sub @@ -3230,37 +4891,245 @@ End Class End Sub - Public Sub Constructor_SemanticError_Partial() - Dim src1 = " + public sub PartialTypes_ConstructorWithInitializerUpdates() + Dim srcA1 = " +Imports System + +Partial Class C + Sub New(arg As Integer) + Console.WriteLine(0) + End Sub + + Sub New(arg As Boolean) + Console.WriteLine(1) + End Sub +End Class +" + Dim srcB1 = " +Imports System + +Partial Class C + Dim a = 1 + + Sub New(arg As UInteger) + Console.WriteLine(2) + End Sub +End Class +" + + Dim srcA2 = " +Imports System + +Partial Class C + Sub New(arg As Integer) + Console.WriteLine(0) + End Sub + + Sub New(arg As Boolean) + Console.WriteLine(1) + End Sub +End Class +" + Dim srcB2 = " +Imports System + +Partial Class C + Dim a = 2 ' updated field initializer + + Sub New(arg As UInteger) + Console.WriteLine(2) + End Sub + + Sub New(arg As Byte) + Console.WriteLine(3) ' new ctor + End Sub +End Class +" + Dim syntaxMapB = GetSyntaxMap(srcB1, srcB2)(0) + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(Function(m) m.Parameters.Single().Type.Name = "Int32"), syntaxMap:=syntaxMapB), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(Function(m) m.Parameters.Single().Type.Name = "Boolean"), syntaxMap:=syntaxMapB), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(Function(m) m.Parameters.Single().Type.Name = "UInt32"), syntaxMap:=syntaxMapB), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(Function(m) m.Parameters.Single().Type.Name = "Byte"), syntaxMap:=Nothing) + }) + }) + End Sub + + + Public Sub PartialTypes_ConstructorWithInitializerUpdates_SemanticErrors() + Dim srcA1 = " +Imports System + +Partial Class C + Sub New(arg As Integer) + Console.WriteLine(0) + End Sub + + Sub New(arg As Integer) + Console.WriteLine(1) + End Sub +End Class +" + Dim srcB1 = " +Imports System + +Partial Class C + Dim a = 1 +End Class +" + + Dim srcA2 = " +Imports System + +Partial Class C + Sub New(arg As Integer) + Console.WriteLine(0) + End Sub + + Sub New(arg As Integer) + Console.WriteLine(1) + End Sub +End Class +" + Dim srcB2 = " +Imports System + +Partial Class C + Dim a = 1 + + Sub New(arg As Integer) + Console.WriteLine(2) + End Sub +End Class +" + + ' The actual edits do not matter since there are semantic errors in the compilation. + ' We just should not crash. + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(diagnostics:=Array.Empty(Of RudeEditDiagnosticDescription)()) + }) + End Sub + + + Public Sub Constructor_SemanticError_Partial() + Dim src1 = " +Partial Class C + Partial Sub New(x As Integer) + End Sub +End Class + +Class C + Partial Sub New(x As Integer) + System.Console.WriteLine(1) + End Sub +End Class +" + Dim src2 = " +Partial Class C + Partial Sub New(x As Integer) + End Sub +End Class + +Class C + Partial Sub New(x As Integer) + System.Console.WriteLine(2) + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics(ActiveStatementsDescription.Empty, semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.First(), preserveLocalVariables:=True) + }) + End Sub + + + public sub PartialDeclaration_Delete() + Dim srcA1 = " +Partial Class C + Sub New() + End Sub + + Sub F() + End Sub +End Class +" + Dim srcB1 = " +Partial Class C + Dim x = 1 +End Class +" + + Dim srcA2 = "" + Dim srcB2 = " +Partial Class C + Dim x = 2 + + Sub F() + End Sub +End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True)}), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F"), partialType:="C"), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True) + }) + }) + End Sub + + + Public Sub PartialDeclaration_Insert() + Dim srcA1 = "" + Dim srcB1 = " Partial Class C - Partial Sub New(x As Integer) - End Sub -End Class + Dim x = 1 -Class C - Partial Sub New(x As Integer) - System.Console.WriteLine(1) + Sub F() End Sub End Class - " - Dim src2 = " + Dim srcA2 = " Partial Class C - Partial Sub New(x As Integer) + Public Sub New() End Sub -End Class -Class C - Partial Sub New(x As Integer) - System.Console.WriteLine(2) + Sub F() End Sub +End Class" + Dim srcB2 = " +Partial Class C + Dim x = 2 End Class " - Dim edits = GetTopEdits(src1, src2) - edits.VerifySemantics(ActiveStatementsDescription.Empty, expectedSemanticEdits:= - { - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Skip(1).First(), preserveLocalVariables:=True) - }) + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F"), partialType:="C"), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True) + }), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True)}) + }) End Sub #End Region @@ -3330,7 +5199,7 @@ End Class "Delete [As Integer]@49") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.method)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.method, "Goo()"))) End Sub @@ -3345,7 +5214,7 @@ End Class "Insert [As Integer]@49") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) End Sub @@ -3360,7 +5229,7 @@ End Class "Insert [As Integer]@57") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Private Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertDllImport, "Private Declare Ansi Function Goo Lib ""Bar"" ()", FeaturesResources.method)) End Sub @@ -3370,7 +5239,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Declare Ansi Sub ExternSub Lib ""ExternDLL""()", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Sub ExternSub Lib ""ExternDLL""()", FeaturesResources.method)) End Sub @@ -3380,7 +5249,23 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Declare Ansi Sub ExternSub Lib ""ExternDLL""", FeaturesResources.method)) + Diagnostic(RudeEditKind.InsertDllImport, "Declare Ansi Sub ExternSub Lib ""ExternDLL""", FeaturesResources.method)) + End Sub + + + Public Sub Declare_DeleteInsert() + Dim srcA1 = "Module M : Declare Ansi Sub ExternSub Lib ""ExternDLL"" : End Module" + Dim srcB1 = "Module M : End Module" + + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults() + }) End Sub #End Region @@ -3398,7 +5283,7 @@ End Class Diagnostic(RudeEditKind.Renamed, "b", FeaturesResources.field)) End Sub - + Public Sub FieldUpdate_Rename2() Dim src1 = "Class C : Dim a1(), b1? As Integer, c1(1,2) As New D() : End Class" Dim src2 = "Class C : Dim a2(), b2? As Integer, c2(1,2) As New D() : End Class" @@ -3440,9 +5325,7 @@ End Class "Move [c]@27 -> @17", "Delete [c]@27") - ' TODO: We could check that the types and order of b and c haven't changed. - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "c", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3458,9 +5341,7 @@ End Class "Delete [c As Object]@27", "Delete [As Object]@29") - ' TODO: We could check that the types and order of b and c haven't changed. - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "c", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3476,9 +5357,7 @@ End Class "Move [c]@17 -> @27", "Insert [As Object]@29") - ' TODO: We could check that the types and order of b and c haven't changed. - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "c", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3492,9 +5371,7 @@ End Class "Update [c As Object]@30 -> [b, c As Object]@27", "Move [b]@17 -> @27") - ' TODO: We could check that the types and order of b and c haven't changed. - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "b", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3508,9 +5385,7 @@ End Class "Update [b, c As Object]@27 -> [c As Object]@30", "Move [b]@27 -> @17") - ' TODO: We could check that the types and order of b and c haven't changed. - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "b", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3522,8 +5397,7 @@ End Class edits.VerifyEdits( "Reorder [b As Object]@27 -> @14") - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "b As Object", FeaturesResources.field)) + edits.VerifySemantics() End Sub @@ -3535,8 +5409,17 @@ End Class edits.VerifyEdits( "Reorder [b, c As Object]@27 -> @14") + edits.VerifySemantics() + End Sub + + + Public Sub Field_VariableMove_TypeChange() + Dim src1 = "Class C : Dim a As Object, b, c As Object : End Class" + Dim src2 = "Class C : Dim a, b As Integer, c As Object : End Class" + Dim edits = GetTopEdits(src1, src2) + edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Move, "b, c As Object", FeaturesResources.field)) + Diagnostic(RudeEditKind.TypeUpdate, "a, b As Integer", FeaturesResources.field)) End Sub @@ -3552,7 +5435,7 @@ End Class "Delete [As Object]@29") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Dim b As Object", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "Dim b As Object", DeletedSymbolDisplay(FeaturesResources.field, "c"))) End Sub @@ -3665,7 +5548,7 @@ End Class "Delete [As Action]@16") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))) End Sub @@ -3683,7 +5566,7 @@ End Class "Delete [As Action]@18") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.event_)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.event_, "a"))) End Sub @@ -3735,6 +5618,22 @@ End Class Diagnostic(RudeEditKind.Move, "Event c", FeaturesResources.event_)) End Sub + + Public Sub EventField_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = "Partial Class C" & vbCrLf & "Event E As System.Action : End Class" + + Dim srcA2 = "Partial Class C" & vbCrLf & "Event E As System.Action : End Class" + Dim srcB2 = "Partial Class C : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults() + }) + End Sub + Public Sub FieldInsert1() Dim src1 = "Class C : End Class" @@ -3751,7 +5650,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "WithEvents F As C", VBFeaturesResources.WithEvents_field)) + Diagnostic(RudeEditKind.InsertVirtual, "F As C", VBFeaturesResources.WithEvents_field)) End Sub @@ -3761,7 +5660,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "G", VBFeaturesResources.WithEvents_field)) + Diagnostic(RudeEditKind.InsertVirtual, "G", VBFeaturesResources.WithEvents_field)) End Sub @@ -3771,25 +5670,25 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "G", VBFeaturesResources.WithEvents_field)) + Diagnostic(RudeEditKind.InsertVirtual, "G As C", VBFeaturesResources.WithEvents_field)) End Sub Public Sub FieldInsert_IntoStruct() Dim src1 = "Structure S : Private a As Integer : End Structure" - Dim src2 = + Dim src2 = " Structure S Private a As Integer Private b As Integer Private Shared c As Integer Private Event d As System.Action End Structure -.Value +" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertIntoStruct, "Private Event d As System.Action", FeaturesResources.event_, VBFeaturesResources.structure_), - Diagnostic(RudeEditKind.InsertIntoStruct, "b", FeaturesResources.field, VBFeaturesResources.structure_), - Diagnostic(RudeEditKind.InsertIntoStruct, "c", FeaturesResources.field, VBFeaturesResources.structure_)) + Diagnostic(RudeEditKind.InsertIntoStruct, "b As Integer", FeaturesResources.field, VBFeaturesResources.structure_), + Diagnostic(RudeEditKind.InsertIntoStruct, "c As Integer", FeaturesResources.field, VBFeaturesResources.structure_)) End Sub @@ -3852,9 +5751,9 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "b", FeaturesResources.field, FeaturesResources.class_), - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "c", FeaturesResources.field, FeaturesResources.class_), - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "d", FeaturesResources.field, FeaturesResources.class_)) + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "b As Integer", FeaturesResources.field, FeaturesResources.class_), + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "c As Integer", FeaturesResources.field, FeaturesResources.class_), + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "d As Integer", FeaturesResources.field, FeaturesResources.class_)) End Sub @@ -3882,9 +5781,71 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "b", FeaturesResources.field, FeaturesResources.class_), - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "c", FeaturesResources.field, FeaturesResources.class_), - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "d", FeaturesResources.field, FeaturesResources.class_)) + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "b As Integer", FeaturesResources.field, FeaturesResources.class_), + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "c As Integer", FeaturesResources.field, FeaturesResources.class_), + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "d As Integer", FeaturesResources.field, FeaturesResources.class_)) + End Sub + + + Public Sub Field_DeleteInsert_LayoutClass_Sequential_OrderPreserved() + Dim src1 = " +Imports System.Runtime.InteropServices + + +Partial Class C + Private a As Integer + Private b As Integer +End Class +" + + Dim src2 = " +Imports System.Runtime.InteropServices + + +Partial Class C + Private a As Integer +End Class + +Partial Class C + Private b As Integer +End Class +" + + Dim edits = GetTopEdits(src1, src2) + + ' TODO: We don't compare the ordering currently. We could allow this edit if the ordering is preserved. + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "b As Integer", FeaturesResources.field, FeaturesResources.class_)) + End Sub + + + Public Sub Field_DeleteInsert_LayoutClass_Sequential_OrderChanged() + Dim src1 = " +Imports System.Runtime.InteropServices + + +Partial Class C + Private a As Integer + Private b As Integer +End Class +" + + Dim src2 = " +Imports System.Runtime.InteropServices + + +Partial Class C + Private b As Integer +End Class + +Partial Class C + Private a As Integer +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "a As Integer", FeaturesResources.field, FeaturesResources.class_)) End Sub @@ -3930,7 +5891,7 @@ End Class End Sub - Public Sub FieldInsert_ParameterlessConstructorInsert_WithInitializersAndLambdas1() + Public Sub FieldInsert_ConstructorReplacingImplicitConstructor_WithInitializersAndLambdas() Dim src1 = " Imports System @@ -3954,7 +5915,7 @@ Class C Dim B As Integer = F(Function(b) b + 1) ' new field - Sub New() ' new ctor + Sub New() ' new ctor replacing existing implicit constructor F(Function(c) c + 1) End Sub End Class @@ -3969,6 +5930,53 @@ End Class SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(), syntaxMap(0))}) End Sub + + Public Sub FieldInsert_ParameterlessConstructorInsert_WithInitializersAndLambdas() + Dim src1 = " +Imports System + +Class C + Shared Function F(x As Func(Of Integer, Integer)) As Integer + Return 1 + End Function + + Dim A = F(Function(a) a + 1) + + Public Sub New(x As Integer) + End Sub +End Class +" + Dim src2 = " +Imports System + +Class C + Shared Function F(x As Func(Of Integer, Integer)) As Integer + Return 1 + End Function + + Dim A = F(Function(a) a + 1) + + Public Sub New(x As Integer) + End Sub + + Public Sub New ' new ctor + F(Function(c) c + 1) + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "Public Sub New")) + + ' TODO (bug https//github.com/dotnet/roslyn/issues/2504): + ' edits.VerifySemantics( + ' ActiveStatementsDescription.Empty, + ' { + ' SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C").Constructors.Single(), syntaxMap(0)) + ' }) + End Sub + Public Sub FieldInsert_ConstructorInsert_WithInitializersAndLambdas1() Dim src1 = " @@ -4002,17 +6010,15 @@ End Class Dim edits = GetTopEdits(src1, src2) Dim syntaxMap = GetSyntaxMap(src1, src2) - edits.VerifySemantics( - ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.B")), - SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single())}) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "Sub New(x As Integer)")) - ' TODO (bug https//github.com/dotnet/roslyn/issues/2504): + ' TODO (bug https://github.com/dotnet/roslyn/issues/2504): 'edits.VerifySemantics( ' ActiveStatementsDescription.Empty, ' { - ' SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")), - ' SemanticEdit(SemanticEditKind.Insert, c => c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(), syntaxMap(0)) + ' SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.B")), + ' SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors.Single(), syntaxMap(0)) ' }) End Sub @@ -4114,6 +6120,27 @@ End Class Diagnostic(RudeEditKind.TypeUpdate, "Property P", FeaturesResources.auto_property)) End Sub + + Public Sub PropertyInsert() + Dim src1 = "Class C : End Class" + Dim src2 = " +Class C + Property P + Get + Return 1 + End Get + Set(value) + End Set + End Property +End Class +" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("P"))}) + End Sub + Public Sub PrivatePropertyAccessorAddSetter() Dim src1 = "Class C : Private _p As Integer : Private ReadOnly Property P As Integer" & vbLf & "Get" & vbLf & "Return 1 : End Get : End Property : End Class" @@ -4162,7 +6189,7 @@ End Class "Delete [As Integer]@97") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Private ReadOnly Property P", VBFeaturesResources.property_accessor)) + Diagnostic(RudeEditKind.Delete, "Private ReadOnly Property P", DeletedSymbolDisplay(VBFeaturesResources.property_accessor, "P(Integer)"))) End Sub @@ -4248,29 +6275,133 @@ Class C Private Property d As Integer Get - Return 0 + Return 0 + End Get + + Set + End Set + End Property + + Private Shared Property e As Integer + Get + Return 0 + End Get + + Set + End Set + End Property +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "Private Property b As Integer", FeaturesResources.auto_property, FeaturesResources.class_), + Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "Private Shared Property c As Integer", FeaturesResources.auto_property, FeaturesResources.class_)) + End Sub + + + Public Sub Property_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = " +Partial Class C + Private Property P As Integer + Get + Return 1 + End Get + Set + End Set + End Property +End Class +" + + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P").GetMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of PropertySymbol)("P").SetMethod) + }), + DocumentResults() + }) + End Sub + + + Public Sub AutoProperty_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = " +Partial Class C + Private Property P As Integer +End Class +" + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults() + }) + End Sub + + + Public Sub AutoPropertyWithInitializer_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = " +Partial Class C + Private Property P As Integer = 1 +End Class +" + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}), + DocumentResults() + }) + End Sub + + + Public Sub Property_Update_LiftedParameter() + Dim src1 = " +Imports System + +Partial Class C + Private Property P(a As Integer) As Integer + Get + Return New Func(Of Integer)(Function() a + 1).Invoke() End Get - Set End Set End Property +End Class +" + Dim src2 = " +Imports System - Private Shared Property e As Integer +Partial Class C + Private Property P(a As Integer) As Integer Get - Return 0 + Return New Func(Of Integer)(Function() 2).Invoke() End Get - Set End Set End Property End Class " + Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "Private Property b As Integer", FeaturesResources.auto_property, FeaturesResources.class_), - Diagnostic(RudeEditKind.InsertIntoClassWithLayout, "Private Shared Property c As Integer", FeaturesResources.auto_property, FeaturesResources.class_)) - End Sub + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")) + End Sub #End Region #Region "Field And Property Initializers" @@ -4339,6 +6470,56 @@ End Class {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub + ''' + ''' It's a semantic error to specify array bunds and initializer at the same time. + ''' EnC analysis needs to handle this case without failing. + ''' + + Public Sub Field_InitializerUpdate_InitializerAndArrayBounds() + Dim src1 = " +Class C + Dim x(1) As Integer = 1 +End Class +" + + Dim src2 = " +Class C + Dim x(2) As Integer = 2 +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + End Sub + + ''' + ''' It's a semantic error to specify array bunds and initializer at the same time. + ''' EnC analysis needs to handle this case without failing. + ''' + + Public Sub Field_InitializerUpdate_AsNewAndArrayBounds() + Dim src1 = " +Class C + Dim x(1) As New C +End Class +" + + Dim src2 = " +Class C + Dim x(2) As New C +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + End Sub + Public Sub Field_InitializerUpdate_AsNew2() Dim src1 = "Class C : Dim a, b As New Decimal(1) : End Class" @@ -4471,7 +6652,7 @@ End Class "Delete [()]@47") edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4487,7 +6668,7 @@ End Class "Delete [()]@51") edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4503,7 +6684,7 @@ End Class "Delete [()]@38") edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4519,7 +6700,7 @@ End Class "Delete [()]@56") edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4535,7 +6716,7 @@ End Class "Delete [()]@43") edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4545,7 +6726,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + Diagnostic(RudeEditKind.ChangingVisibility, "Class C", DeletedSymbolDisplay(FeaturesResources.constructor, "New()"))) End Sub @@ -4555,7 +6736,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.constructor)) + Diagnostic(RudeEditKind.ChangingVisibility, "Class C", DeletedSymbolDisplay(FeaturesResources.constructor, "New()"))) End Sub @@ -4565,7 +6746,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4575,7 +6756,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single())}) + {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -4948,8 +7129,10 @@ End Class Dim src2 = "Partial Class C : Dim a = 2 : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "a = 2", FeaturesResources.field)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub @@ -4958,8 +7141,10 @@ End Class Dim src2 = "Partial Class C : Property a = 2 : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "Property a = 2", FeaturesResources.auto_property)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub @@ -4968,8 +7153,10 @@ End Class Dim src2 = "Partial Class C : Dim a = 2 : End Class : Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "a = 2", FeaturesResources.field)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub @@ -4978,8 +7165,10 @@ End Class Dim src2 = "Partial Class C : Property a = 2 : End Class : Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "Property a = 2", FeaturesResources.auto_property)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub @@ -4988,8 +7177,10 @@ End Class Dim src2 = "Class C : Dim a = 2 : End Class : Partial Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "a = 2", FeaturesResources.field)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub @@ -4998,29 +7189,53 @@ End Class Dim src2 = "Class C : Property a = 2 : End Class : Partial Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.PartialTypeInitializerUpdate, "Property a = 2", FeaturesResources.auto_property)) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single, preserveLocalVariables:=True) + }) End Sub - - Public Sub PrivateFieldInsert1() + + + + + + + + + Public Sub Field_Insert(modifiers As String) Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Private a As Integer = 1 : End Class" + Dim src2 = "Class C : " & modifiers & " a As Integer = 1 : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits( - "Insert [Private a As Integer = 1]@10", - "Insert [a As Integer = 1]@18", - "Insert [a]@18", - "Insert [As Integer]@20") + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("a")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + End Sub - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + + + + + + + + Public Sub Property_Insert(accessibility As String) + Dim src1 = "Class C : End Class" + Dim src2 = "Class C : " & accessibility & " Property a As Integer = 1 : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("a")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) End Sub - - Public Sub PrivateFieldInsert2() + + Public Sub Field_Insert_MultiDeclaration() Dim src1 = "Class C : Private a As Integer = 1 : End Class" Dim src2 = "Class C : Private a, b As Integer : End Class" Dim edits = GetTopEdits(src1, src2) @@ -5035,101 +7250,135 @@ End Class End Sub - Public Sub PrivatePropertyInsert() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Private Property a As Integer = 1 : End Class" + Public Sub Field_Insert_MultiDeclaration_AsNew() + Dim src1 = "Class C : Private a As C = New C : End Class" + Dim src2 = "Class C : Private a, b As New C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [Private Property a As Integer = 1]@10", - "Insert [As Integer]@29") + "Update [a As C = New C]@18 -> [a, b As New C]@18", + "Insert [b]@21", + "Delete [As C]@20") - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + ' TODO: allow this edit + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, "a, b As New C", VBFeaturesResources.as_clause)) End Sub - Public Sub PrivateFieldInsert_Untyped() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Private a = 1 : End Class" + Public Sub Field_Insert_MultiDeclaration_Split() + Dim src1 = "Class C : Private a, b As Integer = 1 : End Class" + Dim src2 = "Class C : Private a As Integer : Private b As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [Private a = 1]@10", - "Insert [a = 1]@18", - "Insert [a]@18") + "Insert [Private b As Integer]@33", + "Update [a, b As Integer = 1]@18 -> [a As Integer]@18", + "Insert [b As Integer]@41", + "Move [b]@21 -> @41", + "Insert [As Integer]@43") - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) End Sub - Public Sub PrivatePropertyInsert_Untyped() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Private Property a = 1 : End Class" + Public Sub Field_DeleteInsert_MultiDeclaration_TypeChange() + Dim src1 = "Class C : Private a, b As Integer = 1 : End Class" + Dim src2 = "Class C : Private a As Integer : Private b As Byte : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits( - "Insert [Private Property a = 1]@10") - - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.TypeUpdate, "b As Byte", FeaturesResources.field)) End Sub - Public Sub PrivateReadOnlyFieldInsert() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Private ReadOnly a As Integer = 1 : End Class" - Dim edits = GetTopEdits(src1, src2) + Public Sub Field_DeleteInsert_Partial_MultiDeclaration_TypeChange() + Dim srcA1 = "Partial Class C : Private a As Integer = 1 : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyEdits( - "Insert [Private ReadOnly a As Integer = 1]@10", - "Insert [a As Integer = 1]@27", - "Insert [a]@27", - "Insert [As Integer]@29") + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : Private a, b As Byte : End Class" - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(diagnostics:= + { + Diagnostic(RudeEditKind.TypeUpdate, "a, b As Byte", FeaturesResources.field) + }) + }) End Sub - Public Sub PublicFieldInsert() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Public a As Integer = 1 : End Class" - Dim edits = GetTopEdits(src1, src2) + Public Sub Field_DeleteInsert_MultiDeclaration_Split() + Dim srcA1 = "Partial Class C : Private a, b As Integer : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyRudeDiagnostics() + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : Private a As Integer = 1 : Private b As Integer = 2 : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub PublicPropertyInsert() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Property a As Integer = 1 : End Class" - Dim edits = GetTopEdits(src1, src2) + Public Sub Field_DeleteInsert_Partial_MultiDeclaration_UpdateArrayBounds() + Dim srcA1 = "Partial Class C : Dim F1(1, 2), F2? As Integer : End Class" + Dim srcB1 = "Partial Class C : End Class" - edits.VerifyRudeDiagnostics() + Dim srcA2 = "Partial Class C : End Class" + Dim srcB2 = "Partial Class C : Dim F1(1, 3), F2? As Integer : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) + }) + }) End Sub - Public Sub ProtectedFieldInsert() + Public Sub FieldInsert_PrivateUntyped() Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Protected a As Integer = 1 : End Class" + Dim src2 = "Class C : Private a = 1 : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyRudeDiagnostics() + edits.VerifyEdits( + "Insert [Private a = 1]@10", + "Insert [a = 1]@18", + "Insert [a]@18") + + edits.VerifySemantics(ActiveStatementsDescription.Empty, + {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub - Public Sub ProtectedPropertyInsert() + Public Sub PropertyInsert_PrivateUntyped() Dim src1 = "Class C : End Class" - Dim src2 = "Class C : Protected Property a As Integer = 1 : End Class" + Dim src2 = "Class C : Private Property a = 1 : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyRudeDiagnostics() + edits.VerifyEdits( + "Insert [Private Property a = 1]@10") + + edits.VerifySemantics(ActiveStatementsDescription.Empty, + {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.a")), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub @@ -5145,7 +7394,7 @@ End Class "Delete [As Integer]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.field)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.field, "a"))) End Sub @@ -5159,7 +7408,7 @@ End Class "Delete [As Integer]@29") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.auto_property)) + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.auto_property, "a"))) End Sub @@ -5873,13 +8122,59 @@ End Class {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), syntaxMap(0))}) End Sub + + Public Sub FieldInitializerUpdate_Lambdas_PartialDeclarationDelete_SingleDocument() + Dim src1 = " +Partial Class C + Dim x = F(Function(a) a + 1) +End Class + +Partial Class C + Dim y = F(Function(a) a + 10) +End Class + +Partial Class C + Public Sub New() + End Sub + + Shared Function F(x As Func(Of Integer, Integer)) + Return 1 + End Function +End Class +" + + Dim src2 = " +Partial Class C + Dim x = F(Function(a) a + 1) +End Class + +Partial Class C + Dim y = F(Function(a) a + 10) + + Shared Function F(x As Func(Of Integer, Integer)) + Return 1 + End Function +End Class +" + Dim edits = GetTopEdits(src1, src2) + + Dim syntaxMap = GetSyntaxMap(src1, src2) + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"), partialType:="C"), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), syntaxMap(0), partialType:="C") + }) + End Sub + Public Sub FieldInitializerUpdate_ActiveStatements1() Dim src1 As String = " Imports System Class C - Dim A As Integer = 1 + Dim A As Integer = 1 Dim B As Integer = 1 Public Sub New(a As Integer) @@ -5895,7 +8190,7 @@ End Class Imports System Class C - Dim A As Integer = 1 + Dim A As Integer = 1 Dim B As Integer = 2 Public Sub New(a As Integer) @@ -5909,12 +8204,12 @@ End Class" Dim edits = GetTopEdits(src1, src2) Dim syntaxMap = GetSyntaxMap(src1, src2) - Dim activeStatements = GetActiveStatements(src1, src2) - edits.VerifySemantics( - activeStatements, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(0), syntaxMap(0)), - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(1), syntaxMap(0))}) + edits.VerifySemantics(semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(0), syntaxMap(0)), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(1), syntaxMap(0)) + }) End Sub @@ -5957,10 +8252,10 @@ Partial Class C End Class " Dim edits = GetTopEdits(src1, src2) - edits.VerifySemantics(ActiveStatementsDescription.Empty, expectedSemanticEdits:= + edits.VerifySemantics(ActiveStatementsDescription.Empty, semanticEdits:= { - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True), - SemanticEdit(SemanticEditKind.Update, Function(c) CType(c.GetMember(Of NamedTypeSymbol)("C").GetMembers("P").Skip(1).First(), IPropertySymbol).GetMethod) + SemanticEdit(SemanticEditKind.Update, Function(c) CType(c.GetMember(Of NamedTypeSymbol)("C").GetMembers("P").First(), IPropertySymbol).GetMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) }) End Sub @@ -6068,7 +8363,7 @@ Imports System.Runtime.InteropServices Class C - Private Custom Event c As Action + Private Custom Event E As Action AddHandler(value As Action) End AddHandler @@ -6081,7 +8376,71 @@ Class C End Class " Dim edits = GetTopEdits(src1, src2) - edits.VerifySemanticDiagnostics() + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("E"))}) + End Sub + + + Public Sub Event_Delete() + Dim src1 = " +Imports System +Imports System.Runtime.InteropServices + +Class C + Private Custom Event E As Action + AddHandler(value As Action) + End AddHandler + + RemoveHandler(value As Action) + End RemoveHandler + + RaiseEvent() + End RaiseEvent + End Event +End Class +" + Dim src2 = " +Imports System +Imports System.Runtime.InteropServices + +Class C +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(FeaturesResources.event_, "E"))) + End Sub + + + Public Sub Event_Partial_InsertDelete() + Dim srcA1 = "Partial Class C : End Class" + Dim srcB1 = " +Partial Class C + Custom Event E As EventHandler + AddHandler(value As EventHandler) + End AddHandler + RemoveHandler(value As EventHandler) + End RemoveHandler + RaiseEvent(sender As Object, e As EventArgs) + End RaiseEvent + End Event +End Class +" + Dim srcA2 = srcB1 + Dim srcB2 = srcA1 + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").AddMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").RemoveMethod), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of EventSymbol)("E").RaiseMethod) + }), + DocumentResults() + }) End Sub #End Region @@ -6536,8 +8895,8 @@ End Class Public Sub MethodTypeParameterInsert1() - Dim src1 = "Class C : " & vbLf & "Public Sub M() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(Of A)() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Public Sub M()" & vbLf & "End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(Of A)()" & vbLf & "End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( @@ -6550,8 +8909,8 @@ End Class Public Sub MethodTypeParameterInsert2() - Dim src1 = "Class C : " & vbLf & "Public Sub M(Of A)() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(Of A, B)() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Public Sub M(Of A)()" & vbLf & "End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(Of A, B)()" & vbLf & "End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( @@ -6578,8 +8937,8 @@ End Class Public Sub MethodTypeParameterDelete2() - Dim src1 = "Class C : " & vbLf & "Public Sub M(Of A, B)() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(Of B)() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Public Sub M(Of A, B)()" & vbLf & "End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(Of B)()" & vbLf & "End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index 0bb91b616e648..84fb2799371c4 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -473,8 +473,8 @@ End Class Dim result = Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newDocument, ImmutableArray(Of TextSpan).Empty, CancellationToken.None) Assert.True(result.HasChanges) - Assert.True(result.SemanticEdits(0).PreserveLocalVariables) Dim syntaxMap = result.SemanticEdits(0).SyntaxMap + Assert.NotNull(syntaxMap) Dim newStatementSpan = result.ActiveStatements(0).Span Dim newStatementTextSpan = newText.Lines.GetTextSpan(newStatementSpan) @@ -662,20 +662,21 @@ End Class End Sub - Public Async Function AnalyzeDocumentAsync_Adding_A_New_File() As Task + Public Async Function AnalyzeDocumentAsync_AddingNewFile() As Task Dim source1 = " -Class C - Public Shared Sub Main() - End Sub -End Class +Namespace N + Class C + Public Shared Sub Main() + End Sub + End Class +End Namespace " Dim source2 = " -Private Class D +Class D End Class " Using workspace = TestWorkspace.CreateVisualBasic(source1, composition:=s_composition) - ' fork the solution to introduce a change Dim oldSolution = workspace.CurrentSolution Dim oldProject = oldSolution.Projects.Single() Dim newDocId = DocumentId.CreateNewId(oldProject.Id) @@ -692,16 +693,15 @@ End Class Dim changedDocuments = changes.GetChangedDocuments().Concat(changes.GetAddedDocuments()) - Dim analyzer = New VisualBasicEditAndContinueAnalyzer() Dim result = New List(Of DocumentAnalysisResults)() Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatement)() + Dim analyzer = New VisualBasicEditAndContinueAnalyzer() For Each changedDocumentId In changedDocuments result.Add(Await analyzer.AnalyzeDocumentAsync(oldProject, baseActiveStatements, newProject.GetDocument(changedDocumentId), ImmutableArray(Of TextSpan).Empty, CancellationToken.None)) Next Assert.True(result.IsSingle()) - Assert.Equal(1, result.Single().RudeEditErrors.Count()) - Assert.Equal(RudeEditKind.InsertFile, result.Single().RudeEditErrors.Single().Kind) + Assert.Empty(result.Single().RudeEditErrors) End Using End Function End Class diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 53401ede16e59..4938de097aeef 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -401,8 +401,8 @@ global statement {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace + + extern alias using directive diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index b6cef0d5793da..4d37f47d11f81 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -90,6 +90,7 @@ private enum SwitchExpressionPart case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.OperatorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: case SyntaxKind.GetAccessorDeclaration: @@ -130,14 +131,14 @@ private enum SwitchExpressionPart } /// - /// Given a node representing a declaration ( = true) or a top-level match node ( = false) returns: + /// Given a node representing a declaration or a top-level match node returns: /// - for method-like member declarations with block bodies (methods, operators, constructors, destructors, accessors). /// - for variable declarators of fields, properties with an initializer expression, or /// for method-like member declarations with expression bodies (methods, properties, indexers, operators) /// /// A null reference otherwise. /// - internal override SyntaxNode? TryGetDeclarationBody(SyntaxNode node, bool isMember) + internal override SyntaxNode? TryGetDeclarationBody(SyntaxNode node) { if (node.IsKind(SyntaxKind.VariableDeclarator, out VariableDeclaratorSyntax? variableDeclarator)) { @@ -493,10 +494,10 @@ internal override SyntaxNode FindPartner(SyntaxNode leftRoot, SyntaxNode rightRo if (leftEqualsClause.Parent.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax? leftDeclaration)) { var leftSymbol = leftModel.GetDeclaredSymbol(leftDeclaration, cancellationToken); - RoslynDebug.Assert(leftSymbol != null); + Contract.ThrowIfNull(leftSymbol); var rightProperty = rightType.GetMembers(leftSymbol.Name).Single(); - var rightDeclaration = (PropertyDeclarationSyntax)GetSymbolSyntax(rightProperty, cancellationToken); + var rightDeclaration = (PropertyDeclarationSyntax)rightProperty.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); rightEqualsClause = rightDeclaration.Initializer; } @@ -504,10 +505,10 @@ internal override SyntaxNode FindPartner(SyntaxNode leftRoot, SyntaxNode rightRo { var leftDeclarator = (VariableDeclaratorSyntax)leftEqualsClause.Parent!; var leftSymbol = leftModel.GetDeclaredSymbol(leftDeclarator, cancellationToken); - RoslynDebug.Assert(leftSymbol != null); + Contract.ThrowIfNull(leftSymbol); var rightField = rightType.GetMembers(leftSymbol.Name).Single(); - var rightDeclarator = (VariableDeclaratorSyntax)GetSymbolSyntax(rightField, cancellationToken); + var rightDeclarator = (VariableDeclaratorSyntax)rightField.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); rightEqualsClause = rightDeclarator.Initializer; } @@ -550,6 +551,14 @@ protected override IEnumerable GetLambdaBodyExpressionsAndStatements protected override Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit) => SyntaxComparer.TopLevel.ComputeMatch(oldCompilationUnit, newCompilationUnit); + protected override Match ComputeTopLevelDeclarationMatch(SyntaxNode oldDeclaration, SyntaxNode newDeclaration) + { + Contract.ThrowIfNull(oldDeclaration.Parent); + Contract.ThrowIfNull(newDeclaration.Parent); + var comparer = new SyntaxComparer(oldDeclaration.Parent, newDeclaration.Parent, new[] { oldDeclaration }, new[] { newDeclaration }); + return comparer.ComputeMatch(oldDeclaration.Parent, newDeclaration.Parent); + } + protected override Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable>? knownMatches) { SyntaxUtilities.AssertIsBody(oldBody, allowLambda: true); @@ -1020,26 +1029,17 @@ internal override bool IsInterfaceDeclaration(SyntaxNode node) => node.IsKind(SyntaxKind.InterfaceDeclaration); internal override SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node) - => node.Parent!.FirstAncestorOrSelf(); + => node.Parent!.FirstAncestorOrSelf(); internal override bool HasBackingField(SyntaxNode propertyOrIndexerDeclaration) => propertyOrIndexerDeclaration.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax? propertyDecl) && SyntaxUtilities.HasBackingField(propertyDecl); - internal override bool IsDeclarationWithInitializer(SyntaxNode declaration) - { - switch (declaration.Kind()) - { - case SyntaxKind.VariableDeclarator: - return ((VariableDeclaratorSyntax)declaration).Initializer != null; + internal override SyntaxNode? TryGetAssociatedMemberDeclaration(SyntaxNode node) + => node.Parent.IsParentKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration, SyntaxKind.EventDeclaration) ? node.Parent.Parent : null; - case SyntaxKind.PropertyDeclaration: - return ((PropertyDeclarationSyntax)declaration).Initializer != null; - - default: - return false; - } - } + internal override bool IsDeclarationWithInitializer(SyntaxNode declaration) + => declaration is VariableDeclaratorSyntax { Initializer: not null } || declaration is PropertyDeclarationSyntax { Initializer: not null }; internal override bool IsConstructorWithMemberInitializers(SyntaxNode constructorDeclaration) => constructorDeclaration is ConstructorDeclarationSyntax ctor && (ctor.Initializer == null || ctor.Initializer.IsKind(SyntaxKind.BaseConstructorInitializer)); @@ -1048,12 +1048,23 @@ internal override bool IsPartial(INamedTypeSymbol type) { var syntaxRefs = type.DeclaringSyntaxReferences; return syntaxRefs.Length > 1 - || ((TypeDeclarationSyntax)syntaxRefs.Single().GetSyntax()).Modifiers.Any(SyntaxKind.PartialKeyword); + || ((BaseTypeDeclarationSyntax)syntaxRefs.Single().GetSyntax()).Modifiers.Any(SyntaxKind.PartialKeyword); } - protected override ISymbol? GetSymbolForEdit(SemanticModel model, SyntaxNode node, EditKind editKind, IReadOnlyDictionary editMap, CancellationToken cancellationToken) + protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference reference, CancellationToken cancellationToken) + => reference.GetSyntax(cancellationToken); + + protected override ISymbol? GetSymbolForEdit( + SemanticModel model, + SyntaxNode node, + EditKind editKind, + IReadOnlyDictionary editMap, + out bool isAmbiguous, + CancellationToken cancellationToken) { - if (node.IsKind(SyntaxKind.Parameter)) + isAmbiguous = false; + + if (node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter, SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) { return null; } @@ -1066,7 +1077,7 @@ internal override bool IsPartial(INamedTypeSymbol type) return null; } - if (node.IsKind(SyntaxKind.IndexerDeclaration) || node.IsKind(SyntaxKind.PropertyDeclaration)) + if (node.IsKind(SyntaxKind.IndexerDeclaration, SyntaxKind.PropertyDeclaration)) { // The only legitimate update of an indexer/property declaration is an update of its expression body. // The expression body itself may have been updated, replaced with an explicit getter, or added to replace an explicit getter. @@ -1081,43 +1092,46 @@ internal override bool IsPartial(INamedTypeSymbol type) return null; } - return model.GetDeclaredSymbol(node, cancellationToken); + var symbol = model.GetDeclaredSymbol(node, cancellationToken); + + // Ignore partial method definition parts. + // Partial method that does not have implementation part is not emitted to metadata. + // Partial method without a definition part is a compilation error. + if (symbol is IMethodSymbol { IsPartialDefinition: true }) + { + return null; + } + + return symbol; } - protected override bool TryGetDeclarationBodyEdit(Edit edit, IReadOnlyDictionary editMap, out SyntaxNode? oldBody, out SyntaxNode? newBody) + protected override void GetUpdatedDeclarationBodies(SyntaxNode oldDeclaration, SyntaxNode newDeclaration, out SyntaxNode? oldBody, out SyntaxNode? newBody) { // Detect a transition between a property/indexer with an expression body and with an explicit getter. // int P => old_body; <-> int P { get { new_body } } // int this[args] => old_body; <-> int this[args] { get { new_body } } // First, return getter or expression body for property/indexer update: - if (edit.Kind == EditKind.Update && (edit.OldNode.IsKind(SyntaxKind.PropertyDeclaration) || edit.OldNode.IsKind(SyntaxKind.IndexerDeclaration))) + if (oldDeclaration.IsKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration)) { - oldBody = SyntaxUtilities.TryGetEffectiveGetterBody(edit.OldNode); - newBody = SyntaxUtilities.TryGetEffectiveGetterBody(edit.NewNode); + oldBody = SyntaxUtilities.TryGetEffectiveGetterBody(oldDeclaration); + newBody = SyntaxUtilities.TryGetEffectiveGetterBody(newDeclaration); if (oldBody != null && newBody != null) { - return true; + return; } } - // Second, ignore deletion of a getter body: - if (IsGetterToExpressionBodyTransformation(edit.Kind, edit.OldNode ?? edit.NewNode, editMap)) - { - oldBody = newBody = null; - return false; - } - - return base.TryGetDeclarationBodyEdit(edit, editMap, out oldBody, out newBody); + base.GetUpdatedDeclarationBodies(oldDeclaration, newDeclaration, out oldBody, out newBody); } private static bool IsGetterToExpressionBodyTransformation(EditKind editKind, SyntaxNode node, IReadOnlyDictionary editMap) { - if ((editKind == EditKind.Insert || editKind == EditKind.Delete) && node.IsKind(SyntaxKind.GetAccessorDeclaration)) + if ((editKind is EditKind.Insert or EditKind.Delete) && node.IsKind(SyntaxKind.GetAccessorDeclaration)) { RoslynDebug.Assert(node.Parent.IsKind(SyntaxKind.AccessorList)); - RoslynDebug.Assert(node.Parent.Parent.IsKind(SyntaxKind.PropertyDeclaration) || node.Parent.Parent.IsKind(SyntaxKind.IndexerDeclaration)); + RoslynDebug.Assert(node.Parent.Parent.IsKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration)); return editMap.TryGetValue(node.Parent, out var parentEdit) && parentEdit == editKind && editMap.TryGetValue(node.Parent!.Parent, out parentEdit) && parentEdit == EditKind.Update; } @@ -1135,7 +1149,7 @@ internal override bool IsLocalFunction(SyntaxNode node) => node.IsKind(SyntaxKind.LocalFunctionStatement); internal override bool IsNestedFunction(SyntaxNode node) - => node is LambdaExpressionSyntax || node is LocalFunctionStatementSyntax; + => node is AnonymousFunctionExpressionSyntax or LocalFunctionStatementSyntax; internal override bool TryGetLambdaBodies(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? body1, out SyntaxNode? body2) => LambdaUtilities.TryGetLambdaBodies(node, out body1, out body2); @@ -1364,6 +1378,7 @@ private static bool GroupBySignatureComparer(ImmutableArray ol case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: case SyntaxKind.UnknownAccessorDeclaration: @@ -1629,7 +1644,7 @@ internal override TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, i return CSharpFeaturesResources.global_statement; case SyntaxKind.ExternAliasDirective: - return CSharpFeaturesResources.using_namespace; + return CSharpFeaturesResources.extern_alias; case SyntaxKind.UsingDirective: // Dev12 distinguishes using alias from using namespace and reports different errors for removing alias. @@ -1675,7 +1690,8 @@ internal override TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, i return FeaturesResources.operator_; case SyntaxKind.ConstructorDeclaration: - return FeaturesResources.constructor; + var ctor = (ConstructorDeclarationSyntax)node; + return ctor.Modifiers.Any(SyntaxKind.StaticKeyword) ? FeaturesResources.static_constructor : FeaturesResources.constructor; case SyntaxKind.DestructorDeclaration: return CSharpFeaturesResources.destructor; @@ -1703,6 +1719,7 @@ internal override TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, i return CSharpFeaturesResources.indexer_getter; } + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: if (node.Parent!.Parent!.IsKind(SyntaxKind.PropertyDeclaration)) { @@ -1954,7 +1971,7 @@ public void ClassifyEdit() return; case EditKind.Move: - ClassifyMove(); + ClassifyMove(_newNode!); return; case EditKind.Insert: @@ -1972,9 +1989,9 @@ public void ClassifyEdit() #region Move and Reorder - private void ClassifyMove() + private void ClassifyMove(SyntaxNode newNode) { - if (_newNode.IsKind(SyntaxKind.LocalFunctionStatement)) + if (newNode.IsKind(SyntaxKind.LocalFunctionStatement)) { return; } @@ -2016,6 +2033,7 @@ private void ClassifyReorder(SyntaxNode newNode) case SyntaxKind.EventDeclaration: case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: case SyntaxKind.TypeParameterConstraintClause: @@ -2064,89 +2082,35 @@ private void ClassifyInsert(SyntaxNode node) case SyntaxKind.ExternAliasDirective: case SyntaxKind.UsingDirective: case SyntaxKind.NamespaceDeclaration: - case SyntaxKind.DestructorDeclaration: ReportError(RudeEditKind.Insert); return; case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: - var typeDeclaration = (TypeDeclarationSyntax)node; - ClassifyTypeWithPossibleExternMembersInsert(typeDeclaration); - return; - case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.DelegateDeclaration: - return; - case SyntaxKind.PropertyDeclaration: - var propertyDeclaration = (PropertyDeclarationSyntax)node; - ClassifyModifiedMemberInsert(propertyDeclaration, propertyDeclaration.Modifiers); - return; - case SyntaxKind.EventDeclaration: - var eventDeclaration = (EventDeclarationSyntax)node; - ClassifyModifiedMemberInsert(eventDeclaration, eventDeclaration.Modifiers); - return; - case SyntaxKind.IndexerDeclaration: - var indexerDeclaration = (IndexerDeclarationSyntax)node; - ClassifyModifiedMemberInsert(indexerDeclaration, indexerDeclaration.Modifiers); - return; - + case SyntaxKind.DestructorDeclaration: case SyntaxKind.ConversionOperatorDeclaration: case SyntaxKind.OperatorDeclaration: - ReportError(RudeEditKind.InsertOperator); - return; - case SyntaxKind.MethodDeclaration: - ClassifyMethodInsert((MethodDeclarationSyntax)node); - return; - case SyntaxKind.ConstructorDeclaration: - // Allow adding parameterless constructor. - // Semantic analysis will determine if it's an actual addition or - // just an update of an existing implicit constructor. - var method = (BaseMethodDeclarationSyntax)node; - if (SyntaxUtilities.IsParameterlessConstructor(method)) - { - // Disallow adding an extern constructor - if (method.Modifiers.Any(SyntaxKind.ExternKeyword)) - { - ReportError(RudeEditKind.InsertExtern); - } - - return; - } - - ClassifyModifiedMemberInsert(method, method.Modifiers); - return; - case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: - ClassifyAccessorInsert((AccessorDeclarationSyntax)node); - return; - - case SyntaxKind.AccessorList: - // an error will be reported for each accessor - return; - case SyntaxKind.FieldDeclaration: case SyntaxKind.EventFieldDeclaration: - // allowed: private fields in classes - ClassifyFieldInsert((BaseFieldDeclarationSyntax)node); - return; - + case SyntaxKind.AccessorList: case SyntaxKind.VariableDeclarator: - // allowed: private fields in classes - ClassifyFieldInsert((VariableDeclaratorSyntax)node); - return; - case SyntaxKind.VariableDeclaration: - // allowed: private fields in classes - ClassifyFieldInsert((VariableDeclarationSyntax)node); + // Adding these members is not allowed or there are limitations on them that needs to be checked. + // However, any of these members can be moved to a different file or partial type declaration, so + // we need to defer reporting rude edits till semantic analysis. return; case SyntaxKind.Parameter when !_classifyStatementSyntax: @@ -2171,100 +2135,6 @@ private void ClassifyInsert(SyntaxNode node) } } - private bool ClassifyModifiedMemberInsert(MemberDeclarationSyntax declaration, SyntaxTokenList modifiers) - { - if (modifiers.Any(SyntaxKind.ExternKeyword)) - { - ReportError(RudeEditKind.InsertExtern); - return false; - } - - var isExplicitlyVirtual = modifiers.Any(SyntaxKind.VirtualKeyword) || modifiers.Any(SyntaxKind.AbstractKeyword) || modifiers.Any(SyntaxKind.OverrideKeyword); - - var isInterfaceVirtual = - declaration.Parent.IsKind(SyntaxKind.InterfaceDeclaration) && - !declaration.IsKind(SyntaxKind.EventFieldDeclaration) && - !declaration.IsKind(SyntaxKind.FieldDeclaration) && - !modifiers.Any(SyntaxKind.SealedKeyword) && - !modifiers.Any(SyntaxKind.StaticKeyword); - - if (isExplicitlyVirtual || isInterfaceVirtual) - { - ReportError(RudeEditKind.InsertVirtual); - return false; - } - - // TODO: Adding a non-virtual member to an interface also fails at runtime - // https://github.com/dotnet/roslyn/issues/37128 - if (declaration.Parent.IsKind(SyntaxKind.InterfaceDeclaration)) - { - ReportError(RudeEditKind.InsertIntoInterface); - return false; - } - - return true; - } - - private void ClassifyTypeWithPossibleExternMembersInsert(TypeDeclarationSyntax type) - { - // extern members are not allowed, even in a new type - foreach (var member in type.Members) - { - var modifiers = default(SyntaxTokenList); - - switch (member.Kind()) - { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.IndexerDeclaration: - case SyntaxKind.EventDeclaration: - modifiers = ((BasePropertyDeclarationSyntax)member).Modifiers; - break; - - case SyntaxKind.ConversionOperatorDeclaration: - case SyntaxKind.OperatorDeclaration: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.ConstructorDeclaration: - modifiers = ((BaseMethodDeclarationSyntax)member).Modifiers; - break; - } - - if (modifiers.Any(SyntaxKind.ExternKeyword)) - { - ReportError(RudeEditKind.InsertExtern, member, member); - } - } - } - - private void ClassifyMethodInsert(MethodDeclarationSyntax method) - { - ClassifyModifiedMemberInsert(method, method.Modifiers); - - if (method.Arity > 0) - { - ReportError(RudeEditKind.InsertGenericMethod); - } - - if (method.ExplicitInterfaceSpecifier != null) - { - ReportError(RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier); - } - } - - private void ClassifyAccessorInsert(AccessorDeclarationSyntax accessor) - { - var baseProperty = (BasePropertyDeclarationSyntax)accessor.Parent!.Parent!; - ClassifyModifiedMemberInsert(baseProperty, baseProperty.Modifiers); - } - - private void ClassifyFieldInsert(BaseFieldDeclarationSyntax field) - => ClassifyModifiedMemberInsert(field, field.Modifiers); - - private void ClassifyFieldInsert(VariableDeclaratorSyntax fieldVariable) - => ClassifyFieldInsert((VariableDeclarationSyntax)fieldVariable.Parent!); - - private void ClassifyFieldInsert(VariableDeclarationSyntax fieldVariable) - => ClassifyFieldInsert((BaseFieldDeclarationSyntax)fieldVariable.Parent!); - #endregion #region Delete @@ -2281,15 +2151,18 @@ private void ClassifyDelete(SyntaxNode oldNode) case SyntaxKind.ExternAliasDirective: case SyntaxKind.UsingDirective: case SyntaxKind.NamespaceDeclaration: + // To allow removal of declarations we would need to update method bodies that + // were previously binding to them but now are binding to another symbol that was previously hidden. + ReportError(RudeEditKind.Delete); + return; + + case SyntaxKind.DestructorDeclaration: + case SyntaxKind.ConversionOperatorDeclaration: + case SyntaxKind.OperatorDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.StructDeclaration: case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.DelegateDeclaration: case SyntaxKind.MethodDeclaration: - case SyntaxKind.ConversionOperatorDeclaration: - case SyntaxKind.OperatorDeclaration: - case SyntaxKind.DestructorDeclaration: case SyntaxKind.PropertyDeclaration: case SyntaxKind.IndexerDeclaration: case SyntaxKind.EventDeclaration: @@ -2297,46 +2170,21 @@ private void ClassifyDelete(SyntaxNode oldNode) case SyntaxKind.EventFieldDeclaration: case SyntaxKind.VariableDeclarator: case SyntaxKind.VariableDeclaration: - // To allow removal of declarations we would need to update method bodies that - // were previously binding to them but now are binding to another symbol that was previously hidden. - ReportError(RudeEditKind.Delete); - return; - + case SyntaxKind.EnumDeclaration: + case SyntaxKind.DelegateDeclaration: case SyntaxKind.ConstructorDeclaration: - // Allow deletion of a parameterless constructor. - // Semantic analysis reports an error if the parameterless ctor isn't replaced by a default ctor. - if (!SyntaxUtilities.IsParameterlessConstructor(oldNode)) - { - ReportError(RudeEditKind.Delete); - } - + // We do not report member delete here since the member might be moving to a different part of a partial type declaration. + // If that is not the case the semantic analysis reports the rude edit. return; case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: - // An accessor can be removed. Accessors are not hiding other symbols. - // If the new compilation still uses the removed accessor a semantic error will be reported. - // For simplicity though we disallow deletion of accessors for now. - // The compiler would need to remember that the accessor has been deleted, - // so that its addition back is interpreted as an update. - // Additional issues might involve changing accessibility of the accessor. - ReportError(RudeEditKind.Delete); - return; - + case SyntaxKind.EnumMemberDeclaration: case SyntaxKind.AccessorList: - Debug.Assert( - oldNode.Parent.IsKind(SyntaxKind.PropertyDeclaration) || - oldNode.Parent.IsKind(SyntaxKind.IndexerDeclaration)); - - var accessorList = (AccessorListSyntax)oldNode; - var setter = accessorList.Accessors.FirstOrDefault(a => a.IsKind(SyntaxKind.SetAccessorDeclaration)); - if (setter != null) - { - ReportError(RudeEditKind.Delete, accessorList.Parent, setter); - } - + // We do not report error here since it will be reported in semantic analysis. return; case SyntaxKind.AttributeList: @@ -2347,12 +2195,6 @@ private void ClassifyDelete(SyntaxNode oldNode) ReportError(RudeEditKind.Delete); return; - case SyntaxKind.EnumMemberDeclaration: - // We could allow removing enum member if it didn't affect the values of other enum members. - // If the updated compilation binds without errors it means that the enum value wasn't used. - ReportError(RudeEditKind.Delete); - return; - case SyntaxKind.TypeParameter: case SyntaxKind.TypeParameterList: case SyntaxKind.TypeParameterConstraintClause: @@ -2465,6 +2307,7 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: ClassifyUpdate((AccessorDeclarationSyntax)oldNode, (AccessorDeclarationSyntax)newNode); @@ -2522,7 +2365,8 @@ private void ClassifyUpdate(TypeDeclarationSyntax oldNode, TypeDeclarationSyntax return; } - if (!SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers)) + // Allow partial keyword to be added or removed. + if (!AreModifiersEquivalent(oldNode.Modifiers, newNode.Modifiers, ignore: SyntaxKind.PartialKeyword)) { ReportError(RudeEditKind.ModifiersUpdate); return; @@ -2534,8 +2378,10 @@ private void ClassifyUpdate(TypeDeclarationSyntax oldNode, TypeDeclarationSyntax return; } - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.BaseList, newNode.BaseList)); - ReportError(RudeEditKind.BaseTypeOrInterfaceUpdate); + if (!SyntaxFactory.AreEquivalent(oldNode.BaseList, newNode.BaseList)) + { + ReportError(RudeEditKind.BaseTypeOrInterfaceUpdate); + } } private void ClassifyUpdate(EnumDeclarationSyntax oldNode, EnumDeclarationSyntax newNode) @@ -2614,7 +2460,7 @@ private void ClassifyUpdate(VariableDeclaratorSyntax oldNode, VariableDeclarator // If the argument lists are mismatched the field must have mismatched "fixed" modifier, // which is reported by the field declaration. - if ((oldNode.ArgumentList == null) == (newNode.ArgumentList == null)) + if (oldNode.ArgumentList is null == newNode.ArgumentList is null) { if (!SyntaxFactory.AreEquivalent(oldNode.ArgumentList, newNode.ArgumentList)) { @@ -2649,7 +2495,8 @@ private void ClassifyUpdate(MethodDeclarationSyntax oldNode, MethodDeclarationSy return; } - if (!ClassifyMethodModifierUpdate(oldNode.Modifiers, newNode.Modifiers)) + // Ignore async keyword when matching modifiers. Async checks are done in ComputeBodyMatch. + if (!AreModifiersEquivalent(oldNode.Modifiers, newNode.Modifiers, ignore: SyntaxKind.AsyncKeyword)) { ReportError(RudeEditKind.ModifiersUpdate); return; @@ -2674,27 +2521,6 @@ private void ClassifyUpdate(MethodDeclarationSyntax oldNode, MethodDeclarationSy containingType: (TypeDeclarationSyntax?)newNode.Parent); } - private static bool ClassifyMethodModifierUpdate(SyntaxTokenList oldModifiers, SyntaxTokenList newModifiers) - { - // Ignore async keyword when matching modifiers. - // async checks are done in ComputeBodyMatch. - - var oldAsyncIndex = oldModifiers.IndexOf(SyntaxKind.AsyncKeyword); - var newAsyncIndex = newModifiers.IndexOf(SyntaxKind.AsyncKeyword); - - if (oldAsyncIndex >= 0) - { - oldModifiers = oldModifiers.RemoveAt(oldAsyncIndex); - } - - if (newAsyncIndex >= 0) - { - newModifiers = newModifiers.RemoveAt(newAsyncIndex); - } - - return SyntaxFactory.AreEquivalent(oldModifiers, newModifiers); - } - private void ClassifyUpdate(ConversionOperatorDeclarationSyntax oldNode, ConversionOperatorDeclarationSyntax newNode) { if (!SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers)) @@ -2960,6 +2786,24 @@ private void ClassifyUpdate(AttributeListSyntax oldNode, AttributeListSyntax new // changes in attribute separators are not interesting: } + private static bool AreModifiersEquivalent(SyntaxTokenList oldModifiers, SyntaxTokenList newModifiers, SyntaxKind ignore) + { + var oldIgnoredModifierIndex = oldModifiers.IndexOf(ignore); + var newIgnoredModifierIndex = newModifiers.IndexOf(ignore); + + if (oldIgnoredModifierIndex >= 0) + { + oldModifiers = oldModifiers.RemoveAt(oldIgnoredModifierIndex); + } + + if (newIgnoredModifierIndex >= 0) + { + newModifiers = newModifiers.RemoveAt(newIgnoredModifierIndex); + } + + return SyntaxFactory.AreEquivalent(oldModifiers, newModifiers); + } + private void ClassifyMethodBodyRudeUpdate( SyntaxNode? oldBody, SyntaxNode? newBody, @@ -3062,9 +2906,94 @@ internal override void ReportMemberUpdateRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol) + internal override void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol, SyntaxNode newNode, bool insertingIntoExistingContainingType) { - // We rejected all exported methods during syntax analysis, so no additional work is needed here. + var rudeEditKind = newSymbol switch + { + // Inserting extern member into a new or existing type is not allowed. + { IsExtern: true } + => RudeEditKind.InsertExtern, + + // All rude edits below only apply when inserting into an existing type (not when the type itself is inserted): + _ when !insertingIntoExistingContainingType => RudeEditKind.None, + + // Inserting a member into an existing generic type is not allowed. + { ContainingType: { Arity: > 0 } } and not INamedTypeSymbol + => RudeEditKind.InsertIntoGenericType, + + // Inserting virtual or interface member into an existing type is not allowed. + { IsVirtual: true } or { IsOverride: true } or { IsAbstract: true } and not INamedTypeSymbol + => RudeEditKind.InsertVirtual, + + // Inserting generic method into an existing type is not allowed. + IMethodSymbol { Arity: > 0 } + => RudeEditKind.InsertGenericMethod, + + // Inserting destructor to an existing type is not allowed. + IMethodSymbol { MethodKind: MethodKind.Destructor } + => RudeEditKind.Insert, + + // Inserting operator to an existing type is not allowed. + IMethodSymbol { MethodKind: MethodKind.Conversion or MethodKind.UserDefinedOperator } + => RudeEditKind.InsertOperator, + + // Inserting a method that explictly implements an interface method into an existing type is not allowed. + IMethodSymbol { ExplicitInterfaceImplementations: { IsEmpty: false } } + => RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier, + + // TODO: Inserting non-virtual member to an interface (https://github.com/dotnet/roslyn/issues/37128) + { ContainingType: { TypeKind: TypeKind.Interface } } and not INamedTypeSymbol + => RudeEditKind.InsertIntoInterface, + + _ => RudeEditKind.None + }; + + if (rudeEditKind != RudeEditKind.None) + { + diagnostics.Add(new RudeEditDiagnostic( + rudeEditKind, + GetDiagnosticSpan(newNode, EditKind.Insert), + newNode, + arguments: new[] { GetDisplayName(newNode, EditKind.Insert) })); + } + } + + internal override void ReportTypeDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, INamedTypeSymbol oldType, INamedTypeSymbol newType, SyntaxNode newDeclaration, CancellationToken cancellationToken) + { + using var _1 = ArrayBuilder.GetInstance(out var oldNodes); + using var _2 = ArrayBuilder.GetInstance(out var newNodes); + + // Consider: better error messages + Report((b, t) => AddNodes(b, t.AttributeLists), RudeEditKind.Update); + Report((b, t) => AddNodes(b, t.TypeParameterList?.Parameters), RudeEditKind.Update); + Report((b, t) => AddNodes(b, t.ConstraintClauses), RudeEditKind.Update); + Report((b, t) => AddNodes(b, t.BaseList?.Types), RudeEditKind.BaseTypeOrInterfaceUpdate); + + void Report(Action, TypeDeclarationSyntax> addNodes, RudeEditKind rudeEditKind) + { + foreach (var syntaxRef in oldType.DeclaringSyntaxReferences) + { + addNodes(oldNodes, (TypeDeclarationSyntax)syntaxRef.GetSyntax(cancellationToken)); + } + + foreach (var syntaxRef in newType.DeclaringSyntaxReferences) + { + addNodes(newNodes, (TypeDeclarationSyntax)syntaxRef.GetSyntax(cancellationToken)); + } + + if (oldNodes.Count != newNodes.Count || + oldNodes.Zip(newNodes, (oldNode, newNode) => SyntaxFactory.AreEquivalent(oldNode, newNode)).Any(isEquivalent => !isEquivalent)) + { + diagnostics.Add(new RudeEditDiagnostic( + rudeEditKind, + GetDiagnosticSpan(newDeclaration, EditKind.Update), + newDeclaration, + arguments: new[] { GetDisplayName(newDeclaration, EditKind.Update) })); + } + + oldNodes.Clear(); + newNodes.Clear(); + } } #endregion diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 4a28976e95174..515e76f771821 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -485,6 +485,7 @@ internal Label Classify(SyntaxKind kind, SyntaxNode? node, out bool isLeaf) case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: isLeaf = true; @@ -815,6 +816,7 @@ public override bool ValuesEqual(SyntaxNode left, SyntaxNode right) case SyntaxKind.DestructorDeclaration: case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: // When comparing method bodies we need to NOT ignore VariableDeclaration and VariableDeclarator children, @@ -1545,6 +1547,7 @@ private static double CombineOptional( case SyntaxKind.GetAccessorDeclaration: case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: return null; case SyntaxKind.TypeParameterConstraintClause: diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs index 1b8b26a678bd4..2fccc1ff4fb88 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxUtilities.cs @@ -38,6 +38,7 @@ static SyntaxNode BlockOrExpression(BlockSyntax blockBodyOpt, ArrowExpressionCla break; case SyntaxKind.SetAccessorDeclaration: + case SyntaxKind.InitAccessorDeclaration: case SyntaxKind.AddAccessorDeclaration: case SyntaxKind.RemoveAccessorDeclaration: case SyntaxKind.GetAccessorDeclaration: diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index 21ee4e5363bbd..a084086132dc6 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -222,6 +222,11 @@ asynchronní deklarace using {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <lambda výraz> @@ -587,11 +592,6 @@ příkaz global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using namespace - - using directive direktiva using diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 6b21c135fdceb..ac01428abc6f3 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -222,6 +222,11 @@ asynchrone using-Deklaration {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <Lambdaausdruck> @@ -587,11 +592,6 @@ global – Anweisung {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - Using-Namespace - - using directive Using-Direktive diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index bce2f1c314cb3..633a55c592b01 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -222,6 +222,11 @@ declaración using asincrónica {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <expresión lambda> @@ -587,11 +592,6 @@ declaración global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - uso de espacio de nombres - - using directive uso de directiva diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 6e0d84b3d51cb..e151efe5d88c0 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -222,6 +222,11 @@ déclaration using asynchrone {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <expression lambda> @@ -587,11 +592,6 @@ instruction global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - espace de noms using - - using directive directive using diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 4bde6e4bac3ae..09d2196abac8e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -222,6 +222,11 @@ dichiarazione using asincrona {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <espressione lambda> @@ -587,11 +592,6 @@ istruzione global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - spazio dei nomi using - - using directive direttiva using diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 3757eabcaaeb5..358fe3893a6a2 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -222,6 +222,11 @@ 非同期の using 宣言 {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <ラムダ式> @@ -587,11 +592,6 @@ global ステートメント {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using namespace - - using directive using ディレクティブ diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 33552037888d1..b996a9e802faf 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -222,6 +222,11 @@ 비동기 using 선언 {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <람다 식> @@ -587,11 +592,6 @@ global 문 {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using 네임스페이스 - - using directive using 지시문 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index ab28e8a0cd3c0..cc430b72cb1f1 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -222,6 +222,11 @@ asynchroniczna deklaracja using {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <wyrażenie lambda> @@ -587,11 +592,6 @@ instrukcja global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - przestrzeń nazw using - - using directive dyrektywa using diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index dde6beb05a878..a0809829b0b83 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -222,6 +222,11 @@ declaração using assíncrona {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <expressão lambda> @@ -587,11 +592,6 @@ instrução global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - usando o namespace - - using directive usando a diretiva diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 7db755f950b9e..8d2a6cdc26bac 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -222,6 +222,11 @@ асинхронное объявление using {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <лямбда-выражение> @@ -587,11 +592,6 @@ инструкция global {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - пространство имен using - - using directive директива using diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 4cfe2851c6a60..71d15b62707de 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -222,6 +222,11 @@ zaman uyumsuz using bildirimi {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> < lambda ifadesi > @@ -587,11 +592,6 @@ global deyimi {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using namespace - - using directive using yönergesi diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 59a8aab8dd156..c09ea9a7428fe 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -222,6 +222,11 @@ 异步 using 声明 {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <lambda 表达式> @@ -587,11 +592,6 @@ global 语句 {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using 命名空间 - - using directive using 指令 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 849cf4e721a03..ba4f7d7d06721 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -222,6 +222,11 @@ 非同步 using 宣告 {Locked="using"} "using" is a C# keyword and should not be localized. + + extern alias + extern alias + + <lambda expression> <Lambda 運算式> @@ -587,11 +592,6 @@ global 陳述式 {Locked="global"} "global" is a C# keyword and should not be localized. - - using namespace - using 命名空間 - - using directive using 指示詞 diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index f6c233dbeac5d..4197c8040823b 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -27,6 +27,42 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz { internal const int DefaultStatementPart = 0; + /// + /// Contains enough information to determine whether two symbols have the same signature. + /// + private static readonly SymbolDisplayFormat s_unqualifiedMemberDisplayFormat = + new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameOnly, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeExplicitInterface, + parameterOptions: + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + private static readonly SymbolDisplayFormat s_fullyQualifiedMemberDisplayFormat = + new( + globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted, + typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, + propertyStyle: SymbolDisplayPropertyStyle.NameOnly, + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters | SymbolDisplayGenericsOptions.IncludeVariance, + memberOptions: + SymbolDisplayMemberOptions.IncludeParameters | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeContainingType, + parameterOptions: + SymbolDisplayParameterOptions.IncludeParamsRefOut | + SymbolDisplayParameterOptions.IncludeExtensionThis | + SymbolDisplayParameterOptions.IncludeType, + miscellaneousOptions: + SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + // used by tests to validate correct handlign of unexpected exceptions private readonly Action? _testFaultInjector; @@ -57,9 +93,7 @@ protected AbstractEditAndContinueAnalyzer(Action? testFaultInjector) /// i.e. a node used as the root of statement-level match. /// /// A node representing a declaration or a top-level edit node. - /// - /// True if represents a member declaration, - /// false if it represents an edit node. + /// /// /// Returns null for nodes that don't represent declarations. /// @@ -71,30 +105,26 @@ protected AbstractEditAndContinueAnalyzer(Action? testFaultInjector) /// /// Body does not need to cover all active statements that may be associated with the member. /// E.g. Body of a C# constructor is the method body block. Active statements may be placed on the base constructor call. - /// Body if a VB field declaration with shared AsNew initializer is the New expression. Active statements might be placed on the field variables. + /// Body of a VB field declaration with shared AsNew initializer is the New expression. Active statements might be placed on the field variables. /// has to account for such cases. /// - internal abstract SyntaxNode? TryGetDeclarationBody(SyntaxNode node, bool isMember); + internal abstract SyntaxNode? TryGetDeclarationBody(SyntaxNode node); /// /// Interprets an edit as a declaration body edit. /// - /// A top-level edit. - /// All top-level edits by syntax node. + /// Old member declaration node. + /// New member declaration node. /// The old body participating in the edit. /// The new body participating in the edit. - /// - /// True if the specified edit is a declaration body edit, false otherwise. - /// - protected virtual bool TryGetDeclarationBodyEdit( - Edit edit, - IReadOnlyDictionary editMap, + protected virtual void GetUpdatedDeclarationBodies( + SyntaxNode oldDeclaration, + SyntaxNode newDeclaration, out SyntaxNode? oldBody, out SyntaxNode? newBody) { - oldBody = (edit.OldNode != null) ? TryGetDeclarationBody(edit.OldNode, isMember: false) : null; - newBody = (edit.NewNode != null) ? TryGetDeclarationBody(edit.NewNode, isMember: false) : null; - return true; + oldBody = TryGetDeclarationBody(oldDeclaration); + newBody = TryGetDeclarationBody(newDeclaration); } /// @@ -172,6 +202,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, TextSpan span, out protected abstract Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit); protected abstract Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable>? knownMatches); + protected abstract Match ComputeTopLevelDeclarationMatch(SyntaxNode oldDeclaration, SyntaxNode newDeclaration); protected abstract IEnumerable GetSyntaxSequenceEdits(ImmutableArray oldNodes, ImmutableArray newNodes); /// @@ -228,7 +259,26 @@ protected virtual bool StateMachineSuspensionPointKindEquals(SyntaxNode suspensi /// protected abstract bool AreEquivalentActiveStatements(SyntaxNode oldStatement, SyntaxNode newStatement, int statementPart); - protected abstract ISymbol? GetSymbolForEdit(SemanticModel model, SyntaxNode node, EditKind editKind, IReadOnlyDictionary editMap, CancellationToken cancellationToken); + /// + /// Returns the symbol associated with an edit of the specified . + /// + /// Semantic model + /// Edit node + /// Edit kind + /// Edit map + /// + /// True if the node edit is associated with multiple symbols. + /// The returned symbol is one of these symbols. + /// All symbols must have the same containing symbol. + /// + /// Cancellation token + protected abstract ISymbol? GetSymbolForEdit( + SemanticModel model, + SyntaxNode node, + EditKind editKind, + IReadOnlyDictionary editMap, + out bool isAmbiguous, + CancellationToken cancellationToken); /// /// Analyzes data flow in the member body represented by the specified node and returns all captured variables and parameters (including "this"). @@ -312,8 +362,9 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract void ReportEnclosingExceptionHandlingRudeEdits(ArrayBuilder diagnostics, IEnumerable> exceptionHandlingEdits, SyntaxNode oldStatement, TextSpan newStatementSpan); internal abstract void ReportOtherRudeEditsAroundActiveStatement(ArrayBuilder diagnostics, Match match, SyntaxNode oldStatement, SyntaxNode newStatement, bool isNonLeaf); internal abstract void ReportMemberUpdateRudeEdits(ArrayBuilder diagnostics, SyntaxNode newMember, TextSpan? span); - internal abstract void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol); + internal abstract void ReportInsertedMemberSymbolRudeEdits(ArrayBuilder diagnostics, ISymbol newSymbol, SyntaxNode newNode, bool insertingIntoExistingContainingType); internal abstract void ReportStateMachineSuspensionPointRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode); + internal abstract void ReportTypeDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, INamedTypeSymbol oldSymbol, INamedTypeSymbol newSymbol, SyntaxNode newDeclaration, CancellationToken cancellationToken); internal abstract bool IsLambda(SyntaxNode node); internal abstract bool IsInterfaceDeclaration(SyntaxNode node); @@ -349,8 +400,19 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract bool TryGetLambdaBodies(SyntaxNode node, [NotNullWhen(true)] out SyntaxNode? body1, out SyntaxNode? body2); internal abstract bool IsStateMachineMethod(SyntaxNode declaration); + + /// + /// Returns the type declaration that contains a specified . + /// This can be class, struct, interface, record or enum declaration. + /// internal abstract SyntaxNode? TryGetContainingTypeDeclaration(SyntaxNode node); + /// + /// Returns a property, indexer or event declaration whose accessor is the specified , + /// or null if is not an accessor. + /// + internal abstract SyntaxNode? TryGetAssociatedMemberDeclaration(SyntaxNode node); + internal abstract bool HasBackingField(SyntaxNode propertyDeclaration); /// @@ -365,6 +427,7 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind internal abstract bool IsConstructorWithMemberInitializers(SyntaxNode declaration); internal abstract bool IsPartial(INamedTypeSymbol type); + internal abstract SyntaxNode EmptyCompilationUnit { get; } private static readonly SourceText s_emptySource = SourceText.From(""); @@ -372,17 +435,20 @@ protected virtual string GetSuspensionPointDisplayName(SyntaxNode node, EditKind #region Document Analysis public async Task AnalyzeDocumentAsync( - Project baseProject, - ImmutableArray baseActiveStatements, - Document document, + Project oldProject, + ImmutableArray oldActiveStatements, + Document newDocument, ImmutableArray newActiveStatementSpans, CancellationToken cancellationToken) { - DocumentAnalysisResults.Log.Write("Analyzing document {0}", document.Name); + DocumentAnalysisResults.Log.Write("Analyzing document {0}", newDocument.Name); Debug.Assert(!newActiveStatementSpans.IsDefault); - Debug.Assert(document.SupportsSyntaxTree); - Debug.Assert(document.SupportsSemanticModel); + Debug.Assert(newDocument.SupportsSyntaxTree); + Debug.Assert(newDocument.SupportsSemanticModel); + + // assume changes until we determine there are none so that EnC is blocked on unexpected exception: + var hasChanges = true; try { @@ -392,7 +458,7 @@ public async Task AnalyzeDocumentAsync( SyntaxNode oldRoot; SourceText oldText; - var oldDocument = baseProject.GetDocument(document.Id); + var oldDocument = oldProject.GetDocument(newDocument.Id); if (oldDocument != null) { oldTree = await oldDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); @@ -408,7 +474,7 @@ public async Task AnalyzeDocumentAsync( oldText = s_emptySource; } - var newTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + var newTree = await newDocument.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(newTree); // Changes in parse options might change the meaning of the code even if nothing else changed. @@ -416,8 +482,8 @@ public async Task AnalyzeDocumentAsync( Debug.Assert(oldTree == null || oldTree.Options.Equals(newTree.Options)); var newRoot = await newTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - var newText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var hasChanges = !oldText.ContentEquals(newText); + var newText = await newDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + hasChanges = !oldText.ContentEquals(newText); _testFaultInjector?.Invoke(newRoot); cancellationToken.ThrowIfCancellationRequested(); @@ -429,15 +495,15 @@ public async Task AnalyzeDocumentAsync( { // Bail, since we can't do syntax diffing on broken trees (it would not produce useful results anyways). // If we needed to do so for some reason, we'd need to harden the syntax tree comparers. - DocumentAnalysisResults.Log.Write("{0}: syntax errors", document.Name); - return DocumentAnalysisResults.SyntaxErrors(document.Id, hasChanges); + DocumentAnalysisResults.Log.Write("{0}: syntax errors", newDocument.Name); + return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, ImmutableArray.Empty, hasChanges); } - var newActiveStatements = ImmutableArray.CreateBuilder(baseActiveStatements.Length); - newActiveStatements.Count = baseActiveStatements.Length; + var newActiveStatements = ImmutableArray.CreateBuilder(oldActiveStatements.Length); + newActiveStatements.Count = oldActiveStatements.Length; - var newExceptionRegions = ImmutableArray.CreateBuilder>(baseActiveStatements.Length); - newExceptionRegions.Count = baseActiveStatements.Length; + var newExceptionRegions = ImmutableArray.CreateBuilder>(oldActiveStatements.Length); + newExceptionRegions.Count = oldActiveStatements.Length; if (!hasChanges) { @@ -447,24 +513,24 @@ public async Task AnalyzeDocumentAsync( // b) we need to ignore errors in unchanged documents AnalyzeUnchangedDocument( - baseActiveStatements, + oldActiveStatements, newText, newRoot, newActiveStatements, newExceptionRegions); - DocumentAnalysisResults.Log.Write("{0}: unchanged", document.Name); - return DocumentAnalysisResults.Unchanged(document.Id, newActiveStatements.MoveToImmutable(), newExceptionRegions.MoveToImmutable()); + DocumentAnalysisResults.Log.Write("{0}: unchanged", newDocument.Name); + return DocumentAnalysisResults.Unchanged(newDocument.Id, newActiveStatements.MoveToImmutable(), newExceptionRegions.MoveToImmutable()); } // Disallow modification of a file with experimental features enabled. // These features may not be handled well by the analysis below. if (ExperimentalFeaturesEnabled(newTree)) { - DocumentAnalysisResults.Log.Write("{0}: experimental features enabled", document.Name); + DocumentAnalysisResults.Log.Write("{0}: experimental features enabled", newDocument.Name); - return DocumentAnalysisResults.Errors(document.Id, activeStatementsOpt: default, ImmutableArray.Create( - new RudeEditDiagnostic(RudeEditKind.ExperimentalFeaturesEnabled, default))); + return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, ImmutableArray.Create( + new RudeEditDiagnostic(RudeEditKind.ExperimentalFeaturesEnabled, default)), hasChanges); } // We do calculate diffs even if there are semantic errors for the following reasons: @@ -473,7 +539,6 @@ public async Task AnalyzeDocumentAsync( // 2) If there are syntactic rude edits we'll report them faster without waiting for semantic analysis. // The user may fix them before they address all the semantic errors. - using var _1 = ArrayBuilder.GetInstance(out var updatedMembers); using var _2 = ArrayBuilder.GetInstance(out var diagnostics); cancellationToken.ThrowIfCancellationRequested(); @@ -481,35 +546,14 @@ public async Task AnalyzeDocumentAsync( var topMatch = ComputeTopLevelMatch(oldRoot, newRoot); var syntacticEdits = topMatch.GetTreeEdits(); var editMap = BuildEditMap(syntacticEdits); - - AnalyzeMemberBodiesSyntax( - syntacticEdits, - editMap, - oldText, - newText, - baseActiveStatements, - newActiveStatementSpans, - newActiveStatements, - newExceptionRegions, - updatedMembers, - diagnostics); + var hasRudeEdits = false; ReportTopLevelSyntacticRudeEdits(diagnostics, syntacticEdits, editMap); - if (diagnostics.Count > 0) - { - DocumentAnalysisResults.Log.Write("{0} syntactic rude edits, first: '{1}'", diagnostics.Count, document.FilePath); - return DocumentAnalysisResults.Errors(document.Id, newActiveStatements.MoveToImmutable(), diagnostics.ToImmutable()); - } - - // Disallow addition of a new file. - // During EnC, a new file cannot be added to the current solution, but some IDE features (i.e., CodeFix) try to do so. - // In most cases, syntactic rude edits detect them with specific reasons but some reach up to here and we bail them out with a general message. - if (oldDocument == null) + if (diagnostics.Count > 0 && !hasRudeEdits) { - DocumentAnalysisResults.Log.Write("A new file added: {0}", document.Name); - return DocumentAnalysisResults.Errors(document.Id, newActiveStatements.MoveToImmutable(), ImmutableArray.Create( - new RudeEditDiagnostic(RudeEditKind.InsertFile, default))); + DocumentAnalysisResults.Log.Write("{0} syntactic rude edits, first: '{1}'", diagnostics.Count, newDocument.FilePath); + hasRudeEdits = true; } cancellationToken.ThrowIfCancellationRequested(); @@ -517,66 +561,64 @@ public async Task AnalyzeDocumentAsync( using var _3 = ArrayBuilder<(SyntaxNode OldNode, SyntaxNode NewNode)>.GetInstance(out var triviaEdits); using var _4 = ArrayBuilder.GetInstance(out var lineEdits); - AnalyzeTrivia( - oldText, - newText, - topMatch, - editMap, - triviaEdits, - lineEdits, - diagnostics, - cancellationToken); - - cancellationToken.ThrowIfCancellationRequested(); - - if (diagnostics.Count > 0) - { - DocumentAnalysisResults.Log.Write("{0} trivia rude edits, first: {1}@{2}", diagnostics.Count, document.FilePath, diagnostics.First().Span.Start); - return DocumentAnalysisResults.Errors(document.Id, newActiveStatements.MoveToImmutable(), diagnostics.ToImmutable()); - } - - cancellationToken.ThrowIfCancellationRequested(); - - var semanticEdits = ImmutableArray.Empty; - if (syntacticEdits.Edits.Length > 0 || triviaEdits.Count > 0) + // Do not analyze trivia in presence of syntactic rude edits. + // The implementation depends on edit map capturing all updates and inserts, + // which might not be the case when rude edits are reported. + if (diagnostics.Count == 0) { - var newModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var oldModel = await oldDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(oldModel); - Contract.ThrowIfNull(newModel); - - using var _ = ArrayBuilder.GetInstance(out var semanticEditsBuilder); - AnalyzeSemantics( - syntacticEdits, - editMap, + AnalyzeTrivia( oldText, - baseActiveStatements, + newText, + topMatch, + editMap, triviaEdits, - updatedMembers, - oldModel, - newModel, - semanticEditsBuilder, + lineEdits, diagnostics, cancellationToken); - cancellationToken.ThrowIfCancellationRequested(); - - if (diagnostics.Count > 0) + if (diagnostics.Count > 0 && !hasRudeEdits) { - DocumentAnalysisResults.Log.Write("{0}@{1}: semantic rude edit ({2} total)", document.FilePath, diagnostics.First().Span.Start, diagnostics.Count); - return DocumentAnalysisResults.Errors(document.Id, newActiveStatements.MoveToImmutable(), diagnostics.ToImmutable()); + DocumentAnalysisResults.Log.Write("{0} trivia rude edits, first: {1}@{2}", diagnostics.Count, newDocument.FilePath, diagnostics.First().Span.Start); + hasRudeEdits = true; } - semanticEdits = semanticEditsBuilder.ToImmutable(); + cancellationToken.ThrowIfCancellationRequested(); + } + + var semanticEdits = await AnalyzeSemanticsAsync( + syntacticEdits, + editMap, + oldText, + newText, + oldActiveStatements, + newActiveStatementSpans, + triviaEdits, + oldProject, + oldDocument, + newDocument, + diagnostics, + newActiveStatements, + newExceptionRegions, + cancellationToken).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + AnalyzeUnchangedMemberBodies(diagnostics, syntacticEdits.Match, oldText, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions); + Debug.Assert(newActiveStatements.All(a => a != null)); + + if (diagnostics.Count > 0 && !hasRudeEdits) + { + DocumentAnalysisResults.Log.Write("{0}@{1}: rude edit ({2} total)", newDocument.FilePath, diagnostics.First().Span.Start, diagnostics.Count); + hasRudeEdits = true; } return new DocumentAnalysisResults( - document.Id, + newDocument.Id, newActiveStatements.MoveToImmutable(), diagnostics.ToImmutable(), - semanticEdits, - newExceptionRegions.MoveToImmutable(), - lineEdits.ToImmutable(), + hasRudeEdits ? default : semanticEdits, + hasRudeEdits ? default : newExceptionRegions.MoveToImmutable(), + hasRudeEdits ? default : lineEdits.ToImmutable(), hasChanges: true, hasSyntaxErrors: false); } @@ -587,10 +629,11 @@ public async Task AnalyzeDocumentAsync( // In such case we report a rude edit for the document. If the host is actually running out of memory, // it might throw another OOM here or later on. var diagnostic = (e is OutOfMemoryException) ? - new RudeEditDiagnostic(RudeEditKind.SourceFileTooBig, span: default, arguments: new[] { document.FilePath }) : - new RudeEditDiagnostic(RudeEditKind.InternalError, span: default, arguments: new[] { document.FilePath, e.ToString() }); + new RudeEditDiagnostic(RudeEditKind.SourceFileTooBig, span: default, arguments: new[] { newDocument.FilePath }) : + new RudeEditDiagnostic(RudeEditKind.InternalError, span: default, arguments: new[] { newDocument.FilePath, e.ToString() }); - return DocumentAnalysisResults.Errors(document.Id, activeStatementsOpt: default, ImmutableArray.Create(diagnostic)); + // Report as "syntax error" - we can't analyze the document + return DocumentAnalysisResults.SyntaxErrors(newDocument.Id, ImmutableArray.Create(diagnostic), hasChanges); } } @@ -602,6 +645,26 @@ private void ReportTopLevelSyntacticRudeEdits(ArrayBuilder d } } + /// + /// Reports rude edits for a symbol that's been deleted in one location and inserted in another and the edit was not classified as + /// or . + /// The scenarios include moving a type declaration from one file to another and moving a member of a partial type from one partial declaration to another. + /// + internal void ReportDeclarationInsertDeleteRudeEdits(ArrayBuilder diagnostics, SyntaxNode oldNode, SyntaxNode newNode) + { + // Consider replacing following syntax analysis with semantic analysis of the corresponding symbols, + // or a combination of semantic and syntax analysis (e.g. primarily analyze symbols but fall back + // to syntax analysis for comparisons of attribute values, optional parameter values, etc.). + // Such approach would likely be simpler and allow us to handle more cases. + + var match = ComputeTopLevelDeclarationMatch(oldNode, newNode); + var syntacticEdits = match.GetTreeEdits(); + var editMap = BuildEditMap(syntacticEdits); + ReportTopLevelSyntacticRudeEdits(diagnostics, syntacticEdits, editMap); + + ReportMemberUpdateRudeEdits(diagnostics, newNode, GetDiagnosticSpan(newNode, EditKind.Update)); + } + internal static Dictionary BuildEditMap(EditScript editScript) { var map = new Dictionary(editScript.Edits.Length); @@ -626,35 +689,7 @@ internal static Dictionary BuildEditMap(EditScript script, - IReadOnlyDictionary editMap, - SourceText oldText, - SourceText newText, - ImmutableArray oldActiveStatements, - ImmutableArray newActiveStatementSpans, - [Out] ImmutableArray.Builder newActiveStatements, - [Out] ImmutableArray>.Builder newExceptionRegions, - [Out] ArrayBuilder updatedMembers, - [Out] ArrayBuilder diagnostics) - { - Debug.Assert(!newActiveStatementSpans.IsDefault); - Debug.Assert(newActiveStatementSpans.IsEmpty || oldActiveStatements.Length == newActiveStatementSpans.Length); - Debug.Assert(oldActiveStatements.Length == newActiveStatements.Count); - Debug.Assert(oldActiveStatements.Length == newExceptionRegions.Count); - Debug.Assert(updatedMembers.Count == 0); - - for (var i = 0; i < script.Edits.Length; i++) - { - AnalyzeChangedMemberBody(script, i, editMap, oldText, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions, updatedMembers, diagnostics); - } - - AnalyzeUnchangedMemberBodies(diagnostics, script.Match, oldText, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions); - - Debug.Assert(newActiveStatements.All(a => a != null)); - } + #region Syntax Analysis private void AnalyzeUnchangedMemberBodies( ArrayBuilder diagnostics, @@ -702,8 +737,8 @@ private void AnalyzeUnchangedMemberBodies( var hasPartner = topMatch.TryGetNewNode(oldMember, out var newMember); Contract.ThrowIfFalse(hasPartner); - var oldBody = TryGetDeclarationBody(oldMember, isMember: true); - var newBody = TryGetDeclarationBody(newMember, isMember: true); + var oldBody = TryGetDeclarationBody(oldMember); + var newBody = TryGetDeclarationBody(newMember); // Guard against invalid active statement spans (in case PDB was somehow out of sync with the source). if (oldBody == null || newBody == null) @@ -768,8 +803,8 @@ private void AnalyzeUnchangedDocument( ImmutableArray oldActiveStatements, SourceText newText, SyntaxNode newRoot, - [In, Out] ImmutableArray.Builder newActiveStatements, - [In, Out] ImmutableArray>.Builder newExceptionRegions) + [Out] ImmutableArray.Builder newActiveStatements, + [Out] ImmutableArray>.Builder newExceptionRegions) { Debug.Assert(oldActiveStatements.Length == newActiveStatements.Count); Debug.Assert(oldActiveStatements.Length == newExceptionRegions.Count); @@ -777,7 +812,7 @@ private void AnalyzeUnchangedDocument( // Active statements in methods that were not updated // are not changed but their spans might have been. - for (var i = 0; i < newActiveStatements.Count; i++) + for (var i = 0; i < oldActiveStatements.Length; i++) { if (!TryGetTextSpan(newText.Lines, oldActiveStatements[i].Span, out var oldStatementSpan) || !TryGetEnclosingBreakpointSpan(newRoot, oldStatementSpan.Start, out var newStatementSpan)) @@ -844,115 +879,33 @@ public LambdaInfo WithMatch(Match match, SyntaxNode newLambdaBody) => new(ActiveNodeIndices, match, newLambdaBody); } - internal readonly struct UpdatedMemberInfo - { - // Index in top edit script. - public readonly int EditOrdinal; - - // node that represents the old body of the method: - public readonly SyntaxNode OldBody; - - // node that represents the new body of the method: - public readonly SyntaxNode NewBody; - - // { NewNode <-> OldNode } - public readonly BidirectionalMap Map; - - // { OldLambdaBody -> LambdaInfo } - public readonly IReadOnlyDictionary? ActiveOrMatchedLambdas; - - // the method has an active statement (the statement might be in the body itself or in a lambda) - public readonly bool HasActiveStatement; - - // The old method body has a suspension point (await/yield); - // only true if the body itself has the suspension point, not if it contains async/iterator lambda - public readonly bool OldHasStateMachineSuspensionPoint; - - // The new method body has a suspension point (await/yield); - // only true if the body itself has the suspension point, not if it contains async/iterator lambda - public readonly bool NewHasStateMachineSuspensionPoint; - - public UpdatedMemberInfo( - int editOrdinal, - SyntaxNode oldBody, - SyntaxNode newBody, - BidirectionalMap map, - IReadOnlyDictionary? activeOrMatchedLambdas, - bool hasActiveStatement, - bool oldHasStateMachineSuspensionPoint, - bool newHasStateMachineSuspensionPoint) - { - Debug.Assert(editOrdinal >= 0); - Debug.Assert(!map.IsDefaultOrEmpty); - - EditOrdinal = editOrdinal; - OldBody = oldBody; - NewBody = newBody; - Map = map; - ActiveOrMatchedLambdas = activeOrMatchedLambdas; - HasActiveStatement = hasActiveStatement; - OldHasStateMachineSuspensionPoint = oldHasStateMachineSuspensionPoint; - NewHasStateMachineSuspensionPoint = newHasStateMachineSuspensionPoint; - } - } - private void AnalyzeChangedMemberBody( - EditScript topEditScript, - int editOrdinal, - IReadOnlyDictionary editMap, + SyntaxNode oldDeclaration, + SyntaxNode newDeclaration, + SyntaxNode oldBody, + SyntaxNode? newBody, SourceText oldText, SourceText newText, + SemanticModel oldModel, + SemanticModel newModel, + ISymbol oldSymbol, + ISymbol newSymbol, ImmutableArray oldActiveStatements, ImmutableArray newActiveStatementSpans, [Out] ImmutableArray.Builder newActiveStatements, [Out] ImmutableArray>.Builder newExceptionRegions, - [Out] ArrayBuilder updatedMembers, - [Out] ArrayBuilder diagnostics) + [Out] ArrayBuilder diagnostics, + out Func? syntaxMap, + CancellationToken cancellationToken) { Debug.Assert(!newActiveStatementSpans.IsDefault); Debug.Assert(newActiveStatementSpans.IsEmpty || oldActiveStatements.Length == newActiveStatementSpans.Length); - Debug.Assert(oldActiveStatements.Length == newActiveStatements.Count); - Debug.Assert(oldActiveStatements.Length == newExceptionRegions.Count); - - var edit = topEditScript.Edits[editOrdinal]; - - // new code can't contain active statements, code that moved doesn't contain updates: - if (edit.Kind == EditKind.Insert || edit.Kind == EditKind.Reorder || edit.Kind == EditKind.Move) - { - return; - } + Debug.Assert(oldActiveStatements.IsEmpty || oldActiveStatements.Length == newActiveStatements.Count); + Debug.Assert(newActiveStatements.Count == newExceptionRegions.Count); - if (!TryGetDeclarationBodyEdit(edit, editMap, out var oldBody, out var newBody) || oldBody == null) - { - return; - } - - var hasActiveStatement = TryGetOverlappingActiveStatements(oldText, edit.OldNode.Span, oldActiveStatements, out var start, out var end); - - if (edit.Kind == EditKind.Delete) - { - // The entire member has been deleted. - - // TODO: if the member isn't a field/property we should return empty span. - // We need to adjust the tracking span design and UpdateUneditedSpans to account for such empty spans. - if (hasActiveStatement) - { - var newSpan = IsDeclarationWithInitializer(edit.OldNode) ? - GetDeletedNodeActiveSpan(topEditScript.Match.Matches, edit.OldNode) : - GetDeletedNodeDiagnosticSpan(topEditScript.Match.Matches, edit.OldNode); - - for (var i = start; i < end; i++) - { - // TODO: VB field multi-initializers break this - // Debug.Assert(newActiveStatements[i] == default(LinePositionSpan)); - - newActiveStatements[i] = oldActiveStatements[i].WithSpan(newText.Lines.GetLinePositionSpan(newSpan)); - newExceptionRegions[i] = ImmutableArray.Create(); - } - } + syntaxMap = null; - return; - } + var hasActiveStatement = TryGetOverlappingActiveStatements(oldText, oldDeclaration.Span, oldActiveStatements, out var start, out var end); if (newBody == null) { @@ -960,7 +913,7 @@ private void AnalyzeChangedMemberBody( if (hasActiveStatement) { - var newSpan = FindClosestActiveSpan(edit.NewNode, DefaultStatementPart); + var newSpan = FindClosestActiveSpan(newDeclaration, DefaultStatementPart); for (var i = start; i < end; i++) { Debug.Assert(newActiveStatements[i] == null && newSpan != default); @@ -1016,7 +969,7 @@ private void AnalyzeChangedMemberBody( { // The tracking span might have been deleted or moved outside of the member span. // It is not an error to move the statement - we just ignore it. - if (trackedSpan.Value.Length != 0 && edit.NewNode.Span.Contains(trackedSpan.Value)) + if (trackedSpan.Value.Length != 0 && newDeclaration.Span.Contains(trackedSpan.Value)) { var newStatementSyntax = FindStatement(newBody, trackedSpan.Value, out var part); Contract.ThrowIfNull(newStatementSyntax); @@ -1039,10 +992,43 @@ private void AnalyzeChangedMemberBody( var bodyMatch = ComputeBodyMatch(oldBody, newBody, activeNodes.Where(n => n.EnclosingLambdaBody == null).ToArray(), diagnostics, out var oldHasStateMachineSuspensionPoint, out var newHasStateMachineSuspensionPoint); var map = ComputeMap(bodyMatch, activeNodes, ref lazyActiveOrMatchedLambdas, diagnostics); - // Save the body match for local variable mapping. - // We'll use it to tell the compiler what local variables to preserve in an active method. - // An edited async/iterator method is considered active. - updatedMembers.Add(new UpdatedMemberInfo(editOrdinal, oldBody, newBody, map, lazyActiveOrMatchedLambdas, hasActiveStatement, oldHasStateMachineSuspensionPoint, newHasStateMachineSuspensionPoint)); + if (oldHasStateMachineSuspensionPoint) + { + ReportStateMachineRudeEdits(oldModel.Compilation, oldSymbol, newBody, diagnostics); + } + + ReportLambdaAndClosureRudeEdits( + oldModel, + oldBody, + newModel, + newBody, + newSymbol, + lazyActiveOrMatchedLambdas, + map, + diagnostics, + out var newBodyHasLambdas, + cancellationToken); + + // We need to provide syntax map to the compiler if + // 1) The new member has a active statement + // The values of local variables declared or synthesized in the method have to be preserved. + // 2) The new member generates a state machine + // In case the state machine is suspended we need to preserve variables. + // 3) The new member contains lambdas + // We need to map new lambdas in the method to the matching old ones. + // If the old method has lambdas but the new one doesn't there is nothing to preserve. + // 4) Constructor that emits initializers is updated. + // We create syntax map even if it's not necessary: if any data member initializers are active/contain lambdas. + // Since initializers are usually simple the map should not be large enough to make it worth optimizing it away. + if (hasActiveStatement || + newHasStateMachineSuspensionPoint || + newBodyHasLambdas || + IsConstructorWithMemberInitializers(newDeclaration) || + IsDeclarationWithInitializer(oldDeclaration) || + IsDeclarationWithInitializer(newDeclaration)) + { + syntaxMap = CreateSyntaxMap(map.Reverse); + } for (var i = 0; i < activeNodes.Length; i++) { @@ -1130,7 +1116,8 @@ private void AnalyzeChangedMemberBody( // rude edit: internal active statement deleted diagnostics.Add( new RudeEditDiagnostic(isNonLeaf ? RudeEditKind.DeleteActiveStatement : RudeEditKind.PartiallyExecutedActiveStatementDelete, - GetDeletedNodeDiagnosticSpan(match.Matches, oldStatementSyntax))); + GetDeletedNodeDiagnosticSpan(match.Matches, oldStatementSyntax), + arguments: new[] { FeaturesResources.code })); } } @@ -1954,6 +1941,11 @@ private static int IndexOfEquivalent(SyntaxNode newNode, List + /// Top-level edit script does not contain edits for a member if only trivia changed in its body. + /// Members that are unchanged but their location in the file changes are not considered updated. + /// This method calculates line and trivia edits for both of these cases. + /// private void AnalyzeTrivia( SourceText oldSource, SourceText newSource, @@ -1964,6 +1956,8 @@ private void AnalyzeTrivia( [Out] ArrayBuilder diagnostics, CancellationToken cancellationToken) { + Debug.Assert(diagnostics.Count == 0); + foreach (var (oldNode, newNode) in topMatch.Matches) { cancellationToken.ThrowIfCancellationRequested(); @@ -1981,10 +1975,14 @@ private void AnalyzeTrivia( continue; } - // The old node matches the new node and body tokens are available for the new node - // hence the old node must have body tokens as well. + // A (rude) edit could have been made that changes whether the node may contain active statements, + // so although the nodes match they might not have the same active tokens. + // E.g. field declaration changed to const field declaration. var oldTokens = TryGetActiveTokens(oldNode); - RoslynDebug.Assert(oldTokens != null); + if (oldTokens == null) + { + continue; + } var newTokensEnum = newTokens.GetEnumerator(); var oldTokensEnum = oldTokens.GetEnumerator(); @@ -2157,7 +2155,10 @@ private readonly struct ConstructorEdit { public readonly INamedTypeSymbol OldType; - // { new field/property initializer or constructor declaration -> syntax map } + /// + /// Contains syntax maps for all changed data member initializers or constructor declarations (of constructors emitting initializers) + /// in the currently analyzed document. The key is the declaration of the member. + /// public readonly Dictionary?> ChangedDeclarations; public ConstructorEdit(INamedTypeSymbol oldType) @@ -2167,414 +2168,606 @@ public ConstructorEdit(INamedTypeSymbol oldType) } } - private void AnalyzeSemantics( + private async Task> AnalyzeSemanticsAsync( EditScript editScript, IReadOnlyDictionary editMap, SourceText oldText, + SourceText newText, ImmutableArray oldActiveStatements, + ImmutableArray newActiveStatementSpans, IReadOnlyList<(SyntaxNode OldNode, SyntaxNode NewNode)> triviaEdits, - IReadOnlyList updatedMembers, - SemanticModel oldModel, - SemanticModel newModel, - [Out] ArrayBuilder semanticEdits, - [Out] ArrayBuilder diagnostics, + Project oldProject, + Document? oldDocument, + Document newDocument, + ArrayBuilder diagnostics, + ImmutableArray.Builder newActiveStatements, + ImmutableArray>.Builder newExceptionRegions, CancellationToken cancellationToken) { + if (editScript.Edits.Length == 0 && triviaEdits.Count == 0) + { + return ImmutableArray.Empty; + } + // { new type -> constructor update } - Dictionary? instanceConstructorEdits = null; - Dictionary? staticConstructorEdits = null; + PooledDictionary? instanceConstructorEdits = null; + PooledDictionary? staticConstructorEdits = null; + + var oldModel = (oldDocument != null) ? await oldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false) : null; + var newModel = await newDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var oldCompilation = oldModel?.Compilation ?? await oldProject.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); + var newCompilation = newModel.Compilation; - INamedTypeSymbol? lazyLayoutAttribute = null; - var newSymbolsWithEdit = new HashSet(); - var updatedMemberIndex = 0; - for (var i = 0; i < editScript.Edits.Length; i++) + using var _1 = PooledHashSet.GetInstance(out var processedSymbols); + using var _2 = ArrayBuilder.GetInstance(out var semanticEdits); + + try { - cancellationToken.ThrowIfCancellationRequested(); + INamedTypeSymbol? lazyLayoutAttribute = null; + for (var i = 0; i < editScript.Edits.Length; i++) + { + cancellationToken.ThrowIfCancellationRequested(); - var edit = editScript.Edits[i]; + var edit = editScript.Edits[i]; - ISymbol? oldSymbol; - ISymbol? newSymbol; - Func? syntaxMap; - SemanticEditKind editKind; + ISymbol? oldSymbol; + ISymbol? newSymbol; + SymbolKey? lazySymbolKey = null; + Func? syntaxMap; + SemanticEditKind editKind; - switch (edit.Kind) - { - case EditKind.Move: - // Move is always a Rude Edit. - throw ExceptionUtilities.UnexpectedValue(edit.Kind); + switch (edit.Kind) + { + case EditKind.Move: + // Move is either a Rude Edit and already reported in syntax analysis, or has no semantic effect. + // For example, in VB we allow move from field multi-declaration. + // "Dim a, b As Integer" -> "Dim a As Integer" (update) and "Dim b As Integer" (move) + continue; - case EditKind.Delete: - { - if (HasParentEdit(editMap, edit)) - { - continue; - } + case EditKind.Reorder: + // Currently we don't do any semantic checks for reordering + // and we don't need to report them to the compiler either. + // Consider: Currently symbol ordering changes are not reflected in metadata (Reflection will report original order). - oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, cancellationToken); - if (oldSymbol == null) - { - continue; - } + // Consider: Reordering of fields is not allowed since it changes the layout of the type. + // This ordering should however not matter unless the type has explicit layout so we might want to allow it. + // We do not check changes to the order if they occur across multiple documents (the containing type is partial). + Debug.Assert(!IsDeclarationWithInitializer(edit.OldNode) && !IsDeclarationWithInitializer(edit.NewNode)); + continue; - // The only member that is allowed to be deleted is a parameterless constructor. - // For any other member a rude edit is reported earlier during syntax edit classification. - // Deleting a parameterless constructor needs special handling. - // If the new type has a parameterless ctor of the same accessibility then UPDATE. - // Error otherwise. + case EditKind.Delete: + { + Contract.ThrowIfNull(oldModel); - Contract.ThrowIfNull(AsParameterlessConstructor(oldSymbol)); + oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, out var oldIsAmbiguous, cancellationToken); + if (oldSymbol == null || oldIsAmbiguous || !processedSymbols.Add(oldSymbol)) + { + // Node doesn't represent a symbol or it represents multiple symbols and the semantic delete + // will be issued for node that represents the specific symbol. + continue; + } - var oldTypeSyntax = TryGetContainingTypeDeclaration(edit.OldNode); - Contract.ThrowIfNull(oldTypeSyntax); + var hasActiveStatement = TryGetOverlappingActiveStatements(oldText, edit.OldNode.Span, oldActiveStatements, out var start, out var end); - var newType = TryGetPartnerType(oldTypeSyntax, editScript.Match, newModel, cancellationToken); + // TODO: if the member isn't a field/property we should return empty span. + // We need to adjust the tracking span design and UpdateUneditedSpans to account for such empty spans. + if (hasActiveStatement) + { + var newSpan = IsDeclarationWithInitializer(edit.OldNode) ? + GetDeletedNodeActiveSpan(editScript.Match.Matches, edit.OldNode) : + GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, edit.OldNode); - // If the type has been deleted we would have reported a rude edit based on syntax analysis and not get here. - Contract.ThrowIfNull(newType); + for (var index = start; index < end; index++) + { + // TODO: VB field multi-initializers break this + // Debug.Assert(newActiveStatements[i] == default(LinePositionSpan)); - newSymbol = TryGetParameterlessConstructor(newType, oldSymbol.IsStatic); + newActiveStatements[index] = oldActiveStatements[index].WithSpan(newText.Lines.GetLinePositionSpan(newSpan)); + newExceptionRegions[index] = ImmutableArray.Create(); + } + } - // If the new constructor is explicitly declared then it must be in another part of a partial type declaration. - // A type can't have more than one parameterless constructor declaration. The current edit is deleting it. - // The new type symbol has a parameterless constructor. Therefore this must be either implicitly declared - // or it is now declared in another part of a partial type declaration. The former case results in - // an update of the constructor symbol. The latter is skipped since the update edit of the constructor will be - // created when the part where the declaration is inserted is analyzed. - if (newSymbol != null && !newSymbol.IsImplicitlyDeclared) - { - continue; - } + syntaxMap = null; + editKind = SemanticEditKind.Delete; - if (newSymbol == null || newSymbol.DeclaredAccessibility != oldSymbol.DeclaredAccessibility) - { - diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.Delete, - GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, edit.OldNode), - edit.OldNode, - new[] { GetDisplayName(edit.OldNode, EditKind.Delete) })); + // Check if the declaration has been moved from one document to another. + var symbolKey = SymbolKey.Create(oldSymbol, cancellationToken); + lazySymbolKey = symbolKey; - continue; - } + // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. + newSymbol = symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (newSymbol != null && !(newSymbol is IMethodSymbol newMethod && newMethod.IsPartialDefinition)) + { + // Symbol has actually not been deleted but rather moved to another document, another partial type declaration + // or replaced with an implicitly generated one (e.g. parameterless constructor, auto-generated record methods, etc.) + + // Report rude edit if the deleted code contains active statements. + // TODO (https://github.com/dotnet/roslyn/issues/51177): + // Only report rude edit when replacing member with an implicit one if it has an active statement. + // We might be able to support moving active members but we would need to + // 1) Move AnalyzeChangedMemberBody from Insert to Delete + // 2) Handle active statements that moved to a different document in ActiveStatementTrackingService + // 3) The debugger's ManagedActiveStatementUpdate might need another field indicating the source file path. + if (hasActiveStatement) + { + ReportDeletedMemberRudeEdit(diagnostics, editScript, edit.OldNode, oldSymbol, RudeEditKind.DeleteActiveStatement); + continue; + } - editKind = SemanticEditKind.Update; - syntaxMap = null; - } + if (!newSymbol.IsImplicitlyDeclared) + { + // Ignore the delete. The new symbol is explicitly declared and thus there will be an insert edit that will issue a semantic update. + continue; + } - break; + // can't change visibility: + if (newSymbol.DeclaredAccessibility != oldSymbol.DeclaredAccessibility) + { + ReportDeletedMemberRudeEdit(diagnostics, editScript, edit.OldNode, oldSymbol, RudeEditKind.ChangingVisibility); + continue; + } - case EditKind.Reorder: - // Currently we don't do any semantic checks for reordering - // and we don't need to report them to the compiler either. + // If a constructor is deleted and replaced by an implicit one the update needs to aggregate updates to all data member initializers. + if (IsConstructorWithMemberInitializers(edit.OldNode)) + { + processedSymbols.Remove(oldSymbol); + DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newDeclaration: null, syntaxMap, oldSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + continue; + } - // Reordering of fields is not allowed since it changes the layout of the type. - Debug.Assert(!IsDeclarationWithInitializer(edit.OldNode) && !IsDeclarationWithInitializer(edit.NewNode)); - continue; + // there is no insert edit for an implicit declaration, therefore we need to issue an update: + editKind = SemanticEditKind.Update; + } + else + { + // Check if the symbol being deleted is a member of a type or associated with a property or event that's also being deleted. + // If so, skip the member deletion and only report the containing symbol deletion. + var oldContainingSymbol = (oldSymbol as IMethodSymbol)?.AssociatedSymbol ?? oldSymbol.ContainingType; + if (oldContainingSymbol != null) + { + var containingSymbolKey = SymbolKey.Create(oldContainingSymbol, cancellationToken); + var newContatiningSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (newContatiningSymbol == null) + { + continue; + } + } - case EditKind.Insert: - { - editKind = SemanticEditKind.Insert; + // deleting symbol is not allowed + var diagnosticSpan = GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, edit.OldNode); - var newTypeSyntax = TryGetContainingTypeDeclaration(edit.NewNode); + diagnostics.Add(new RudeEditDiagnostic( + RudeEditKind.Delete, + diagnosticSpan, + edit.OldNode, + new[] + { + string.Format(FeaturesResources.member_kind_and_name, + GetDisplayName(edit.OldNode, EditKind.Delete), + oldSymbol.ToDisplayString(diagnosticSpan.IsEmpty ? s_fullyQualifiedMemberDisplayFormat : s_unqualifiedMemberDisplayFormat)) + })); - if (newTypeSyntax != null && HasEdit(editMap, newTypeSyntax, EditKind.Insert)) - { - // inserting into a new type - continue; + continue; + } } - syntaxMap = null; - oldSymbol = null; - newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, cancellationToken); + break; - if (newSymbol == null) + case EditKind.Insert: { - // node doesn't represent a symbol - continue; - } + Contract.ThrowIfNull(newModel); - // TODO: scripting - // inserting a top-level member/type - if (newTypeSyntax == null) - { - break; - } + syntaxMap = null; + oldSymbol = null; + newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, out var newIsAmbiguous, cancellationToken); + if (newSymbol == null || newIsAmbiguous || !processedSymbols.Add(newSymbol)) + { + // Node doesn't represent a symbol or it represents multiple symbols and the semantic insert + // will be issued for node that represents the specific symbol. + continue; + } - var newType = (INamedTypeSymbol)newModel.GetRequiredDeclaredSymbol(newTypeSyntax, cancellationToken); - var oldType = TryGetPartnerType(newTypeSyntax, editScript.Match, oldModel, cancellationToken); - - // There has to be a matching old type syntax since the containing type hasn't been inserted. - Contract.ThrowIfNull(oldType); - Contract.ThrowIfNull(newType); - - ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol); - ReportTypeLayoutUpdateRudeEdits(diagnostics, newSymbol, edit.NewNode, newModel, ref lazyLayoutAttribute); - - // Inserting a parameterless constructor needs special handling: - // 1) static ctor - // a) old type has an implicit static ctor - // UPDATE of the implicit static ctor - // b) otherwise - // INSERT of a static parameterless ctor - // - // 2) public instance ctor - // a) old type has an implicit instance ctor - // UPDATE of the implicit instance ctor - // b) otherwise - // ERROR: adding a non-private member - // 3) non-public instance ctor - // a) old type has an implicit instance ctor - // ERROR: changing visibility of the ctor - // b) otherwise - // INSERT of an instance parameterless ctor - - var newCtor = AsParameterlessConstructor(newSymbol); - if (newCtor != null) - { - oldSymbol = TryGetParameterlessConstructor(oldType, newSymbol.IsStatic); + editKind = SemanticEditKind.Insert; + INamedTypeSymbol? oldContainingType; + var newContainingType = newSymbol.ContainingType; + + // Check if the declaration has been moved from one document to another. + var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); + lazySymbolKey = symbolKey; - if (newCtor.IsStatic) + // Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation. + oldSymbol = symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (oldSymbol != null) { - if (oldSymbol != null) + // Symbol has actually not been inserted but rather moved between documents or partial type declarations, + // or is replacing an implicitly generated one (e.g. parameterless constructor, auto-generated record methods, etc.) + oldContainingType = oldSymbol.ContainingType; + + if (oldSymbol.IsImplicitlyDeclared) { - editKind = SemanticEditKind.Update; + // Replace implicit declaration with an explicit one. + + if (oldSymbol.DeclaredAccessibility != newSymbol.DeclaredAccessibility) + { + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.ChangingVisibility, + GetDiagnosticSpan(edit.NewNode, edit.Kind), + arguments: new[] { GetDisplayName(edit.NewNode, edit.Kind) })); + + continue; + } } - } - else if (oldSymbol != null) - { - if (oldSymbol.DeclaredAccessibility != newCtor.DeclaredAccessibility) + else if (newSymbol is IFieldSymbol { ContainingType: { TypeKind: TypeKind.Enum } }) + { + // Skip enum field declarations. Enums can't be partial their fields must be inserted at the same time as the enum itself. + continue; + } + else if (newSymbol is INamedTypeSymbol { TypeKind: not (TypeKind.Delegate or TypeKind.Enum) } newTypeSymbol) { - // changing visibility of a member - diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.ChangingConstructorVisibility, - GetDiagnosticSpan(edit.NewNode, EditKind.Insert))); + // The old symbol must be named type as well since we resolved it via symbol key above. + var oldTypeSymbol = (INamedTypeSymbol)oldSymbol; + + // The types have multiple partial declaration parts, each can contribute attributes and base types. + // All have to declare the same type parameters, but each can add different attributes to them. + // Only one can contribute generic type parameter constraints. + // We collect all these entities and require them to be unchanged. + ReportTypeDeclarationInsertDeleteRudeEdits(diagnostics, oldTypeSymbol, newTypeSymbol, edit.NewNode, cancellationToken); + + continue; } - else + else if (oldSymbol.DeclaringSyntaxReferences.Length == 1 && newSymbol.DeclaringSyntaxReferences.Length == 1) { + // We ignore partial method definition parts when processing edits (GetSymbolForEdit). + // The only declaration in compilation without syntax errors that can have multiple declaring references is a type declaration. + // We can therefore ignore any symbols that have more than one declaration. + ReportTypeLayoutUpdateRudeEdits(diagnostics, newSymbol, edit.NewNode, newModel, ref lazyLayoutAttribute); + + // Compare the old declaration syntax of the symbol with its new declaration and report rude edits + // if it changed in any way that's not allowed. + var oldNode = GetSymbolDeclarationSyntax(oldSymbol.DeclaringSyntaxReferences[0], cancellationToken); + var newNode = edit.NewNode; + + ReportDeclarationInsertDeleteRudeEdits(diagnostics, oldNode, newNode); + + var oldBody = TryGetDeclarationBody(oldNode); + var newBody = TryGetDeclarationBody(newNode); + if (oldBody == null && newBody == null) + { + continue; + } + + if (oldBody != null) + { + // The old symbol's declaration syntax may be located in a different document than the old version of the current document. + var oldSyntaxDocument = oldProject.Solution.GetRequiredDocument(oldNode.SyntaxTree); + var oldSyntaxModel = await oldSyntaxDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var oldSyntaxText = await oldSyntaxDocument.GetTextAsync(cancellationToken).ConfigureAwait(false); + + // Skip analysis of active statements. We already report rude edit for removal of code containing + // active statements in the old declaration and don't currently support moving active statements. + AnalyzeChangedMemberBody( + oldNode, + newNode, + oldBody, + newBody, + oldSyntaxText, + newText, + oldSyntaxModel, + newModel, + oldSymbol, + newSymbol, + oldActiveStatements: ImmutableArray.Empty, + newActiveStatementSpans: ImmutableArray.Empty, + newActiveStatements, + newExceptionRegions, + diagnostics, + out syntaxMap, + cancellationToken); + } + + // If a constructor changes from including initializers to not including initializers + // we don't need to aggregate syntax map from all initializers for the constructor update semantic edit. + if (IsConstructorWithMemberInitializers(newNode) || + IsDeclarationWithInitializer(oldNode) || + IsDeclarationWithInitializer(newNode)) + { + processedSymbols.Remove(newSymbol); + DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newNode, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + + // Don't add a separate semantic edit. + // Updates of data members with initializers and constructors that emit initializers will be aggregated and added later. + continue; + } + editKind = SemanticEditKind.Update; } } - } - - var isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.NewNode); - if (isConstructorWithMemberInitializers || IsDeclarationWithInitializer(edit.NewNode)) - { - if (DeferConstructorEdit( - oldType, - newType, - editKind, - edit.NewNode, - newSymbol, - newModel, - isConstructorWithMemberInitializers, - ref syntaxMap, - ref instanceConstructorEdits, - ref staticConstructorEdits, - diagnostics, - cancellationToken)) + else if (newSymbol.ContainingType != null) { - if (newSymbol.Kind == SymbolKind.Method) + // The edit actually adds a new symbol into an existing or a new type. + + // If the symbol is an accessor and the containing property/indexer/event declaration has also been inserted skip + // the insert of the accessor as it will be inserted by the property/indexer/event. + var newAssociatedMemberDeclaration = TryGetAssociatedMemberDeclaration(edit.NewNode); + if (newAssociatedMemberDeclaration != null && HasEdit(editMap, newAssociatedMemberDeclaration, EditKind.Insert)) { - // Don't add a separate semantic edit for a field/property with an initializer. - // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted. continue; } - else + + var containingSymbolKey = SymbolKey.Create(newContainingType, cancellationToken); + oldContainingType = containingSymbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol as INamedTypeSymbol; + + // Check rude edits for each member even if it is inserted into a new type. + ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol, edit.NewNode, insertingIntoExistingContainingType: oldContainingType != null); + + if (oldContainingType == null) { - // A semantic edit to create the field/property is gonna be added. - Contract.ThrowIfFalse(editKind == SemanticEditKind.Insert); + // Insertion of a new symbol into a new type. + // We'll produce a single insert edit for the entire type. + continue; } + + // Report rude edits for changes to data member changes of a type with an explicit layout. + // We disallow moving a data member of a partial type with explicit layout even when it actually does not change the layout. + // We could compare the exact order of the members but the scenario is unlikely to occur. + ReportTypeLayoutUpdateRudeEdits(diagnostics, newSymbol, edit.NewNode, newModel, ref lazyLayoutAttribute); } - } - } + else + { + // adds a new top-level type + Contract.ThrowIfFalse(newSymbol is INamedTypeSymbol); - break; + oldContainingType = null; + ReportInsertedMemberSymbolRudeEdits(diagnostics, newSymbol, edit.NewNode, insertingIntoExistingContainingType: false); + } - case EditKind.Update: - { - editKind = SemanticEditKind.Update; + var isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.NewNode); + if (isConstructorWithMemberInitializers || IsDeclarationWithInitializer(edit.NewNode)) + { + Contract.ThrowIfNull(newContainingType); + Contract.ThrowIfNull(oldContainingType); + + // TODO (bug https://github.com/dotnet/roslyn/issues/2504) + if (isConstructorWithMemberInitializers && + editKind == SemanticEditKind.Insert && + IsPartial(newContainingType) && + HasMemberInitializerContainingLambda(oldContainingType, newSymbol.IsStatic, cancellationToken)) + { + // rude edit: Adding a constructor to a type with a field or property initializer that contains an anonymous function + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, GetDiagnosticSpan(edit.NewNode, EditKind.Insert))); + break; + } - // An updated member info was added for a subset of edits in the order of the edits. - // Fetch the next info if it matches the current edit ordinal. - UpdatedMemberInfo? updatedMemberOpt; - if (updatedMemberIndex < updatedMembers.Count && updatedMembers[updatedMemberIndex].EditOrdinal == i) - { - updatedMemberOpt = updatedMembers[updatedMemberIndex++]; - } - else - { - updatedMemberOpt = null; + DeferConstructorEdit(oldContainingType, newContainingType, edit.NewNode, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + + if (isConstructorWithMemberInitializers || editKind == SemanticEditKind.Update) + { + processedSymbols.Remove(newSymbol); + + // Don't add a separate semantic edit. + // Edits of data members with initializers and constructors that emit initializers will be aggregated and added later. + continue; + } + + // A semantic edit to create the field/property is gonna be added. + Contract.ThrowIfFalse(editKind == SemanticEditKind.Insert); + } } - newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, cancellationToken); - if (newSymbol == null) + break; + + case EditKind.Update: { - // node doesn't represent a symbol - Contract.ThrowIfTrue(updatedMemberOpt.HasValue); - continue; - } + Contract.ThrowIfNull(oldModel); + Contract.ThrowIfNull(newModel); - oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, cancellationToken); - Contract.ThrowIfNull(oldSymbol); + newSymbol = GetSymbolForEdit(newModel, edit.NewNode, edit.Kind, editMap, out var newIsAmbiguous, cancellationToken); + if (newSymbol == null || !processedSymbols.Add(newSymbol)) + { + // node doesn't represent a symbol or the symbol has already been processed + continue; + } - var oldContainingType = oldSymbol.ContainingType; - var newContainingType = newSymbol.ContainingType; + editKind = SemanticEditKind.Update; + syntaxMap = null; + oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, out var oldIsAmbiguous, cancellationToken); + if (oldSymbol == null) + { + // May happen when the old node represents partial method changed from a definition to an implementation (adding a body). + // This is already reported as rude edit. + continue; + } - if (updatedMemberOpt.HasValue) - { - var updatedMember = updatedMemberOpt.Value; - - ReportStateMachineRudeEdits(oldModel.Compilation, updatedMember, oldSymbol, diagnostics); - - ReportLambdaAndClosureRudeEdits( - oldModel, - updatedMember.OldBody, - newModel, - updatedMember.NewBody, - newSymbol, - updatedMember.ActiveOrMatchedLambdas, - updatedMember.Map, - diagnostics, - out var newBodyHasLambdas, - cancellationToken); - - // We need to provide syntax map to the compiler if - // 1) The new member has a active statement - // The values of local variables declared or synthesized in the method have to be preserved. - // 2) The new member generates a state machine - // In case the state machine is suspended we need to preserve variables. - // 3) The new member contains lambdas - // We need to map new lambdas in the method to the matching old ones. - // If the old method has lambdas but the new one doesn't there is nothing to preserve. - if (updatedMember.HasActiveStatement || updatedMember.NewHasStateMachineSuspensionPoint || newBodyHasLambdas) + GetUpdatedDeclarationBodies(edit.OldNode, edit.NewNode, out var oldBody, out var newBody); + if (oldBody != null) { - syntaxMap = CreateSyntaxMap(updatedMember.Map.Reverse); + AnalyzeChangedMemberBody( + edit.OldNode, + edit.NewNode, + oldBody, + newBody, + oldText, + newText, + oldModel, + newModel, + oldSymbol, + newSymbol, + oldActiveStatements, + newActiveStatementSpans, + newActiveStatements, + newExceptionRegions, + diagnostics, + out syntaxMap, + cancellationToken); } - else + + // If a constructor changes from including initializers to not including initializers + // we don't need to aggregate syntax map from all initializers for the constructor update semantic edit. + if (IsConstructorWithMemberInitializers(edit.NewNode) || + IsDeclarationWithInitializer(edit.OldNode) || + IsDeclarationWithInitializer(edit.NewNode)) { - syntaxMap = null; + processedSymbols.Remove(newSymbol); + DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, edit.NewNode, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + + // Don't add a separate semantic edit. + // Updates of data members with initializers and constructors that emit initializers will be aggregated and added later. + continue; } - } - else - { - syntaxMap = null; - } - // If a constructor changes from including initializers to not including initializers - // we don't need to aggregate syntax map from all initializers for the constructor update semantic edit. - var isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(edit.NewNode); - if (isConstructorWithMemberInitializers || - IsDeclarationWithInitializer(edit.OldNode) || - IsDeclarationWithInitializer(edit.NewNode)) - { - if (DeferConstructorEdit( - oldContainingType, - newContainingType, - editKind, - edit.NewNode, - newSymbol, - newModel, - isConstructorWithMemberInitializers, - ref syntaxMap, - ref instanceConstructorEdits, - ref staticConstructorEdits, - diagnostics, - cancellationToken)) + // Do not create a semantic update if the edit is not specific to a single symbol. + // The update might still affect a constructor update processed above. + if (oldIsAmbiguous || newIsAmbiguous) { - // Don't add a separate semantic edit for a field/property with an initializer. - // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted. continue; } } + + break; + + default: + throw ExceptionUtilities.UnexpectedValue(edit.Kind); + } + + Contract.ThrowIfFalse(editKind is SemanticEditKind.Update or SemanticEditKind.Insert); + + if (editKind == SemanticEditKind.Update) + { + // The only update to the type itself that's supported is an addition or removal of the partial modifier, + // which does not have impact on the emitted type metadata. + if (newSymbol is INamedTypeSymbol) + { + continue; } - break; + // The property itself is being updated. Currently we do not allow any modifiers or attributes to be updated, + // so the only case when this happens is in C# for a property/indexer that has an expression body. + // The symbol that's actually being updated is the getter. + // TODO: This will need to be revisited in https://github.com/dotnet/roslyn/issues/48628 + if (newSymbol is IPropertySymbol { GetMethod: var propertyGetter and not null }) + { + newSymbol = propertyGetter; + lazySymbolKey = null; + } + } - default: - throw ExceptionUtilities.UnexpectedValue(edit.Kind); + lazySymbolKey ??= SymbolKey.Create(newSymbol, cancellationToken); + + // Edits in data member initializers and constructors are deferred, edits of other members (even on partial types) + // do not need merging accross partial type declarations. + semanticEdits.Add(new SemanticEditInfo(editKind, lazySymbolKey.Value, syntaxMap, partialType: null)); } - semanticEdits.Add(new SemanticEdit(editKind, oldSymbol, newSymbol, syntaxMap, preserveLocalVariables: syntaxMap != null)); - newSymbolsWithEdit.Add(newSymbol); - } + foreach (var (oldNode, newNode) in triviaEdits) + { + Contract.ThrowIfNull(oldModel); + Contract.ThrowIfNull(newModel); - foreach (var (oldNode, newNode) in triviaEdits) - { - var oldSymbol = GetSymbolForEdit(oldModel, oldNode, EditKind.Update, editMap, cancellationToken); - var newSymbol = GetSymbolForEdit(newModel, newNode, EditKind.Update, editMap, cancellationToken); + var newSymbol = GetSymbolForEdit(newModel, newNode, EditKind.Update, editMap, out var newIsAmbiguous, cancellationToken); - // Trivia edits are only calculated for members bodies and each member has a symbol. - RoslynDebug.Assert(oldSymbol != null); - RoslynDebug.Assert(newSymbol != null); + // Trivia edits are only calculated for member bodies and each member has a symbol. + Contract.ThrowIfNull(newSymbol); - var oldContainingType = oldSymbol.ContainingType; - var newContainingType = newSymbol.ContainingType; + if (!processedSymbols.Add(newSymbol)) + { + // symbol already processed + continue; + } - // We need to provide syntax map to the compiler if the member is active (see member update above): - var isActiveMember = - TryGetOverlappingActiveStatements(oldText, oldNode.Span, oldActiveStatements, out var start, out var end) || - IsStateMachineMethod(oldNode) || - ContainsLambda(oldNode); + var oldSymbol = GetSymbolForEdit(oldModel, oldNode, EditKind.Update, editMap, out var oldIsAmbiguous, cancellationToken); + Contract.ThrowIfNull(oldSymbol); - var syntaxMap = isActiveMember ? CreateSyntaxMapForEquivalentNodes(oldNode, newNode) : null; + var oldContainingType = oldSymbol.ContainingType; + var newContainingType = newSymbol.ContainingType; - // only trivia changed: - Debug.Assert(IsConstructorWithMemberInitializers(oldNode) == IsConstructorWithMemberInitializers(newNode)); - Debug.Assert(IsDeclarationWithInitializer(oldNode) == IsDeclarationWithInitializer(newNode)); + // We need to provide syntax map to the compiler if the member is active (see member update above): + var isActiveMember = + TryGetOverlappingActiveStatements(oldText, oldNode.Span, oldActiveStatements, out var start, out var end) || + IsStateMachineMethod(oldNode) || + ContainsLambda(oldNode); - var isConstructorWithMemberInitializers = IsConstructorWithMemberInitializers(newNode); - if (isConstructorWithMemberInitializers || - IsDeclarationWithInitializer(newNode)) - { - if (DeferConstructorEdit( - oldContainingType, - newContainingType, - SemanticEditKind.Update, - newNode, - newSymbol, - newModel, - isConstructorWithMemberInitializers, - ref syntaxMap, - ref instanceConstructorEdits, - ref staticConstructorEdits, - diagnostics, - cancellationToken)) + var syntaxMap = isActiveMember ? CreateSyntaxMapForEquivalentNodes(oldNode, newNode) : null; + + // only trivia changed: + Contract.ThrowIfFalse(IsConstructorWithMemberInitializers(oldNode) == IsConstructorWithMemberInitializers(newNode)); + Contract.ThrowIfFalse(IsDeclarationWithInitializer(oldNode) == IsDeclarationWithInitializer(newNode)); + + if (IsConstructorWithMemberInitializers(newNode) || IsDeclarationWithInitializer(newNode)) { - // Don't add a separate semantic edit for a field/property with an initializer. - // All edits of initializers will be aggregated to edits of constructors where these initializers are emitted. + // TODO: only create syntax map if any field initializers are active/contain lambdas or this is a partial type + syntaxMap ??= CreateSyntaxMapForEquivalentNodes(oldNode, newNode); + + processedSymbols.Remove(newSymbol); + DeferConstructorEdit(oldContainingType, newContainingType, newNode, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); + + // Don't add a separate semantic edit. + // Updates of data members with initializers and constructors that emit initializers will be aggregated and added later. continue; } + + // Do not create a semantic update if the edit is not specific to a single symbol. + // The update might still affect a constructor update processed above. + if (oldIsAmbiguous || newIsAmbiguous) + { + continue; + } + + // Edits in data member initializers and constructors are deferred, edits of other members (even on partial types) + // do not need merging accross partial type declarations. + var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, partialType: null)); } - semanticEdits.Add(new SemanticEdit(SemanticEditKind.Update, oldSymbol, newSymbol, syntaxMap, isActiveMember)); - newSymbolsWithEdit.Add(newSymbol); - } + if (instanceConstructorEdits != null) + { + AddConstructorEdits( + instanceConstructorEdits, + editScript.Match, + oldModel, + oldCompilation, + processedSymbols, + isStatic: false, + semanticEdits, + diagnostics, + cancellationToken); + } - if (instanceConstructorEdits != null) - { - AddConstructorEdits( - instanceConstructorEdits, - editScript.Match, - oldModel, - newSymbolsWithEdit, - isStatic: false, - semanticEdits: semanticEdits, - diagnostics: diagnostics, - cancellationToken: cancellationToken); + if (staticConstructorEdits != null) + { + AddConstructorEdits( + staticConstructorEdits, + editScript.Match, + oldModel, + oldCompilation, + processedSymbols, + isStatic: true, + semanticEdits, + diagnostics, + cancellationToken); + } } - - if (staticConstructorEdits != null) + finally { - AddConstructorEdits( - staticConstructorEdits, - editScript.Match, - oldModel, - newSymbolsWithEdit, - isStatic: true, - semanticEdits: semanticEdits, - diagnostics: diagnostics, - cancellationToken: cancellationToken); + instanceConstructorEdits?.Free(); + staticConstructorEdits?.Free(); } + + return semanticEdits.ToImmutable(); + } + + private void ReportDeletedMemberRudeEdit( + ArrayBuilder diagnostics, + EditScript editScript, + SyntaxNode oldNode, + ISymbol oldSymbol, + RudeEditKind rudeEditKind) + { + diagnostics.Add(new RudeEditDiagnostic( + rudeEditKind, + GetDeletedNodeDiagnosticSpan(editScript.Match.Matches, oldNode), + arguments: new[] + { + string.Format(FeaturesResources.member_kind_and_name, GetDisplayName(oldNode, EditKind.Delete), oldSymbol.ToDisplayString(s_unqualifiedMemberDisplayFormat)) + })); } #region Type Layout Update Validation @@ -2684,32 +2877,8 @@ private static bool HasExplicitOrSequentialLayout( #endregion - private static INamedTypeSymbol? TryGetPartnerType(SyntaxNode typeSyntax, Match topMatch, SemanticModel partnerModel, CancellationToken cancellationToken) - { - SyntaxNode partner; - if (topMatch.OldRoot.SyntaxTree == typeSyntax.SyntaxTree) - { - _ = topMatch.TryGetNewNode(typeSyntax, out partner); - } - else - { - _ = topMatch.TryGetOldNode(typeSyntax, out partner); - } - - if (partner == null) - { - return null; - } - - Debug.Assert(partner.SyntaxTree == partnerModel.SyntaxTree); - - return (INamedTypeSymbol?)partnerModel.GetDeclaredSymbol(partner, cancellationToken); - } - private Func CreateSyntaxMapForEquivalentNodes(SyntaxNode oldRoot, SyntaxNode newRoot) { - Debug.Assert(AreEquivalent(newRoot, oldRoot)); - return newNode => newRoot.FullSpan.Contains(newNode.SpanStart) ? FindPartner(newRoot, oldRoot, newNode) : null; } @@ -2717,15 +2886,6 @@ private static bool HasExplicitOrSequentialLayout( private static Func CreateSyntaxMap(IReadOnlyDictionary reverseMap) => newNode => reverseMap.TryGetValue(newNode, out var oldNode) ? oldNode : null; - private Func CreateSyntaxMapForPartialTypeConstructor( - INamedTypeSymbol oldType, - INamedTypeSymbol newType, - SemanticModel newModel, - Func? ctorSyntaxMap) - { - return newNode => ctorSyntaxMap?.Invoke(newNode) ?? FindPartnerInMemberInitializer(newModel, newType, newNode, oldType, default); - } - private Func? CreateAggregateSyntaxMap( IReadOnlyDictionary reverseTopMatches, IReadOnlyDictionary?> changedDeclarations) @@ -2736,7 +2896,7 @@ private static bool HasExplicitOrSequentialLayout( var newDeclaration = FindMemberDeclaration(root: null, newNode); // the syntax map is only used for nodes that are contained in a declaration - RoslynDebug.Assert(newDeclaration != null); + Contract.ThrowIfNull(newDeclaration); // The node is in a field, property or constructor declaration that has been changed: if (changedDeclarations.TryGetValue(newDeclaration, out var syntaxMap)) @@ -2759,86 +2919,26 @@ private static bool HasExplicitOrSequentialLayout( #region Constructors and Initializers - private static IMethodSymbol? AsParameterlessConstructor(ISymbol symbol) - { - if (symbol.Kind != SymbolKind.Method) - { - return null; - } - - var method = (IMethodSymbol)symbol; - var kind = method.MethodKind; - if (kind != MethodKind.Constructor && kind != MethodKind.StaticConstructor) - { - return null; - } - - return method.Parameters.Length == 0 ? method : null; - } - /// /// Called when a body of a constructor or an initializer of a member is updated or inserted. /// - private bool DeferConstructorEdit( + private static void DeferConstructorEdit( INamedTypeSymbol oldType, INamedTypeSymbol newType, - SemanticEditKind editKind, - SyntaxNode newDeclaration, - ISymbol newSymbol, - SemanticModel newModel, - bool isConstructor, - ref Func? syntaxMap, - ref Dictionary? instanceConstructorEdits, - ref Dictionary? staticConstructorEdits, - [Out] ArrayBuilder diagnostics, - CancellationToken cancellationToken) + SyntaxNode? newDeclaration, + Func? syntaxMap, + bool isStatic, + ref PooledDictionary? instanceConstructorEdits, + ref PooledDictionary? staticConstructorEdits) { - if (IsPartial(newType)) - { - // Since we don't calculate match across partial declarations we need to disallow - // adding and updating fields/properties with initializers of a partial type declaration. - // Assuming this restriction we can allow editing all constructors of partial types. - // The ones that include initializers won't differ in the field initialization. - - if (!isConstructor) - { - // rude edit: Editing a field/property initializer of a partial type. - diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.PartialTypeInitializerUpdate, - newDeclaration.Span, - newDeclaration, - new[] { GetDisplayName(newDeclaration, EditKind.Update) })); - return false; - } - - // TODO (bug https://github.com/dotnet/roslyn/issues/2504) - if (editKind == SemanticEditKind.Insert) - { - if (HasMemberInitializerContainingLambda(oldType, newSymbol.IsStatic, cancellationToken)) - { - // rude edit: Adding a constructor to a type with a field or property initializer that contains an anonymous function - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); - return false; - } - - syntaxMap = null; - } - else - { - syntaxMap = CreateSyntaxMapForPartialTypeConstructor(oldType, newType, newModel, syntaxMap); - } - - return false; - } - Dictionary constructorEdits; - if (newSymbol.IsStatic) + if (isStatic) { - constructorEdits = staticConstructorEdits ??= new(); + constructorEdits = staticConstructorEdits ??= PooledDictionary.GetInstance(); } else { - constructorEdits = instanceConstructorEdits ??= new(); + constructorEdits = instanceConstructorEdits ??= PooledDictionary.GetInstance(); } if (!constructorEdits.TryGetValue(newType, out var constructorEdit)) @@ -2846,48 +2946,69 @@ private bool DeferConstructorEdit( constructorEdits.Add(newType, constructorEdit = new ConstructorEdit(oldType)); } - constructorEdit.ChangedDeclarations.Add(newDeclaration, syntaxMap); - - return true; + if (newDeclaration != null) + { + constructorEdit.ChangedDeclarations.Add(newDeclaration, syntaxMap); + } } private void AddConstructorEdits( - Dictionary updatedTypes, + IReadOnlyDictionary updatedTypes, Match topMatch, - SemanticModel oldModel, - HashSet newSymbolsWithEdit, + SemanticModel? oldModel, + Compilation oldCompilation, + IReadOnlySet processedSymbols, bool isStatic, - [Out] ArrayBuilder semanticEdits, + [Out] ArrayBuilder semanticEdits, [Out] ArrayBuilder diagnostics, CancellationToken cancellationToken) { - foreach (var (newType, update) in updatedTypes) + var oldSyntaxTree = topMatch.OldRoot.SyntaxTree; + var newSyntaxTree = topMatch.NewRoot.SyntaxTree; + + foreach (var (newType, updatesInCurrentDocument) in updatedTypes) { - var oldType = update.OldType; + var oldType = updatesInCurrentDocument.OldType; + + var anyInitializerUpdatesInCurrentDocument = updatesInCurrentDocument.ChangedDeclarations.Keys.Any(IsDeclarationWithInitializer); - Debug.Assert(!IsPartial(oldType)); - Debug.Assert(!IsPartial(newType)); + // If any of the partial declarations of the new or the old type are in another document + // the edit will need to be merged with other partial edits with matching partial type + static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) + => reference.SyntaxTree != syntaxTree; - var anyInitializerUpdates = update.ChangedDeclarations.Keys.Any(IsDeclarationWithInitializer); + var isPartialEdit = + oldType.DeclaringSyntaxReferences.Any(IsNotInDocument, oldSyntaxTree) || + newType.DeclaringSyntaxReferences.Any(IsNotInDocument, newSyntaxTree); + + // Create a syntax map that aggregates syntax maps of the constructor body and all initializers in this document. + // Use syntax maps stored in update.ChangedDeclarations and fallback to 1:1 map for unchanged members. + // + // This aggregated map will be combined with similar maps capturing members declared in partial type declarations + // located in other documents when the semantic edits are merged across all changed documents of the project. + // + // We will create an aggregate syntax map even in cases when we don't necessarily need it, + // for example if none of the edited declarations are active. It's ok to have a map that we don't need. + // This is simpler than detecting whether or not some of the initializers/constructors contain active statements. + var aggregateSyntaxMap = CreateAggregateSyntaxMap(topMatch.ReverseMatches, updatesInCurrentDocument.ChangedDeclarations); bool? lazyOldTypeHasMemberInitializerContainingLambda = null; foreach (var newCtor in isStatic ? newType.StaticConstructors : newType.InstanceConstructors) { - if (newSymbolsWithEdit.Contains(newCtor)) + if (processedSymbols.Contains(newCtor)) { // we already have an edit for the new constructor continue; } + var newCtorKey = SymbolKey.Create(newCtor, cancellationToken); + ISymbol? oldCtor; if (!newCtor.IsImplicitlyDeclared) { // Constructors have to have a single declaration syntax, they can't be partial - var newDeclaration = FindMemberDeclaration(root: null, GetSymbolSyntax(newCtor, cancellationToken)); - - // Partial types were filtered out previously and rude edits reported. - RoslynDebug.Assert(newDeclaration?.SyntaxTree == topMatch.NewRoot.SyntaxTree); + var newDeclaration = GetSymbolDeclarationSyntax(newCtor.DeclaringSyntaxReferences.Single(), cancellationToken); // Constructor that doesn't contain initializers had a corresponding semantic edit produced previously // or was not edited. In either case we should not produce a semantic edit for it. @@ -2898,62 +3019,87 @@ private void AddConstructorEdits( // If no initializer updates were made in the type we only need to produce semantic edits for constructors // whose body has been updated, otherwise we need to produce edits for all constructors that include initializers. - if (!anyInitializerUpdates && !update.ChangedDeclarations.ContainsKey(newDeclaration)) + // If changes were made to initializers or constructors of a partial type in another document they will be merged + // when aggregating semantic edits from all changed documents. Rude edits resulting from those changes, if any, will + // be reported in the document they were made in. + if (!anyInitializerUpdatesInCurrentDocument && !updatesInCurrentDocument.ChangedDeclarations.ContainsKey(newDeclaration)) { continue; } + // To avoid costly SymbolKey resolution we first try to match the constructor in the current document + // and special case parameter-less constructor. if (topMatch.TryGetOldNode(newDeclaration, out var oldDeclaration)) { - // If the constructor wasn't explicitly edited and its body edit is disallowed report an error. - var diagnosticCount = diagnostics.Count; - ReportMemberUpdateRudeEdits(diagnostics, newDeclaration, span: null); - if (diagnostics.Count > diagnosticCount) - { - continue; - } - + Contract.ThrowIfNull(oldModel); oldCtor = oldModel.GetDeclaredSymbol(oldDeclaration, cancellationToken); - Debug.Assert(oldCtor != null); + Contract.ThrowIfNull(oldCtor); } else if (newCtor.Parameters.Length == 0) { oldCtor = TryGetParameterlessConstructor(oldType, isStatic); } else + { + var resolution = newCtorKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken); + + // There may be semantic errors in the compilation that result in multiple candidates. + // Pick the first candidate. + + oldCtor = resolution.Symbol; + } + + if (oldCtor == null && HasMemberInitializerContainingLambda(oldType, isStatic, ref lazyOldTypeHasMemberInitializerContainingLambda, cancellationToken)) { // TODO (bug https://github.com/dotnet/roslyn/issues/2504) - if (HasMemberInitializerContainingLambda(oldType, isStatic, ref lazyOldTypeHasMemberInitializerContainingLambda, cancellationToken)) - { - // rude edit: Adding a constructor to a type with a field or property initializer that contains an anonymous function - diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); - continue; - } + // rude edit: Adding a constructor to a type with a field or property initializer that contains an anonymous function + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, GetDiagnosticSpan(newDeclaration, EditKind.Insert))); + continue; + } + + // Report an error if the updated constructor's declaration is in the current document + // and its body edit is disallowed (e.g. contains stackalloc). + if (oldCtor != null && newDeclaration?.SyntaxTree == newSyntaxTree && anyInitializerUpdatesInCurrentDocument) + { + // attribute rude edit to one of the modified members + var firstSpan = updatesInCurrentDocument.ChangedDeclarations.Keys.Where(IsDeclarationWithInitializer).Aggregate( + (min: int.MaxValue, span: default(TextSpan)), + (accumulate, node) => (node.SpanStart < accumulate.min) ? (node.SpanStart, node.Span) : accumulate).span; - // no initializer contains lambdas => we don't need a syntax map - oldCtor = null; + Contract.ThrowIfTrue(firstSpan.IsEmpty); + ReportMemberUpdateRudeEdits(diagnostics, newDeclaration, firstSpan); } } else { + // New constructor is implicitly declared so it must be parameterless. + // + // Instance constructor: + // Its presence indicates there are no other instance constructors in the new type and therefore + // there must be a single parameterless instance constructor in the old type (constructors with parameters can't be removed). + // + // Static constructor: + // Static constructor is always parameterless and not implicitly generated if there are no static initializers. oldCtor = TryGetParameterlessConstructor(oldType, isStatic); + Contract.ThrowIfFalse(isStatic || oldCtor != null); } - // We assume here that the type is not partial and thus we collected all changed - // field and property initializers in update.ChangedDeclarations and we only need - // the current top match to map nodes from all unchanged initializers. - // - // We will create an aggregate syntax map even in cases when we don't necessarily need it, - // for example if none of the edited declarations are active. It's ok to have a map that we don't need. - var aggregateSyntaxMap = (oldCtor != null && update.ChangedDeclarations.Count > 0) ? - CreateAggregateSyntaxMap(topMatch.ReverseMatches, update.ChangedDeclarations) : null; - - semanticEdits.Add(new SemanticEdit( - (oldCtor == null) ? SemanticEditKind.Insert : SemanticEditKind.Update, - oldCtor, - newCtor, - aggregateSyntaxMap, - preserveLocalVariables: aggregateSyntaxMap != null)); + if (oldCtor != null) + { + semanticEdits.Add(new SemanticEditInfo( + SemanticEditKind.Update, + newCtorKey, + aggregateSyntaxMap, + partialType: isPartialEdit ? SymbolKey.Create(newType, cancellationToken) : null)); + } + else + { + semanticEdits.Add(new SemanticEditInfo( + SemanticEditKind.Insert, + newCtorKey, + syntaxMap: null, + partialType: null)); + } } } } @@ -2979,7 +3125,7 @@ private bool HasMemberInitializerContainingLambda(INamedTypeSymbol type, bool is (member.Kind == SymbolKind.Field || member.Kind == SymbolKind.Property) && member.DeclaringSyntaxReferences.Length > 0) // skip generated fields (e.g. VB auto-property backing fields) { - var syntax = GetSymbolSyntax(member, cancellationToken); + var syntax = GetSymbolDeclarationSyntax(member.DeclaringSyntaxReferences.Single(), cancellationToken); if (IsDeclarationWithInitializer(syntax) && ContainsLambda(syntax)) { return true; @@ -3016,28 +3162,30 @@ private void ReportLambdaAndClosureRudeEdits( IReadOnlyDictionary? matchedLambdas, BidirectionalMap map, ArrayBuilder diagnostics, - out bool newBodyHasLambdas, + out bool syntaxMapRequired, CancellationToken cancellationToken) { + syntaxMapRequired = false; + if (matchedLambdas != null) { var anySignatureErrors = false; foreach (var (oldLambdaBody, newLambdaInfo) in matchedLambdas) { - // The map now contains only matched lambdas. Any unmatched ones would have contained an active statement and - // a rude edit would be reported in syntax analysis phase. - RoslynDebug.Assert(newLambdaInfo.Match != null && newLambdaInfo.NewBody != null); + // Any unmatched lambdas would have contained an active statement and a rude edit would be reported in syntax analysis phase. + // Skip the rest of lambda and closure analysis if such lambdas are present. + if (newLambdaInfo.Match == null || newLambdaInfo.NewBody == null) + { + return; + } ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaInfo.NewBody, diagnostics, out var hasErrors, cancellationToken); anySignatureErrors |= hasErrors; } ArrayBuilder? lazyNewErroneousClauses = null; - foreach (var entry in map.Forward) + foreach (var (oldQueryClause, newQueryClause) in map.Forward) { - var oldQueryClause = entry.Key; - var newQueryClause = entry.Value; - if (!QueryClauseLambdasTypeEquivalent(oldModel, oldQueryClause, newModel, newQueryClause, cancellationToken)) { lazyNewErroneousClauses ??= ArrayBuilder.GetInstance(); @@ -3066,7 +3214,6 @@ select clausesByQuery.First()) // only dig into captures if lambda signatures match if (anySignatureErrors) { - newBodyHasLambdas = true; return; } } @@ -3100,7 +3247,6 @@ select clausesByQuery.First()) if (anyCaptureErrors) { - newBodyHasLambdas = true; return; } @@ -3172,7 +3318,6 @@ select clausesByQuery.First()) if (mappedLambdasHaveErrors) { - newBodyHasLambdas = true; return; } } @@ -3200,8 +3345,6 @@ select clausesByQuery.First()) // We currently allow #1, #2, and #3 and report a rude edit for the other cases. // In future we might be able to enable more. - newBodyHasLambdas = false; - var containingTypeDeclaration = TryGetContainingTypeDeclaration(newMemberBody); var isInInterfaceDeclaration = containingTypeDeclaration != null && IsInterfaceDeclaration(containingTypeDeclaration); @@ -3227,7 +3370,7 @@ select clausesByQuery.First()) } } - newBodyHasLambdas = true; + syntaxMapRequired = true; } } @@ -3350,8 +3493,10 @@ private static void BuildIndex(Dictionary index, ImmutableArray } } - protected static SyntaxNode GetSymbolSyntax(ISymbol local, CancellationToken cancellationToken) - => local.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); + /// + /// Returns node that represents a declaration of the symbol whose is passed in. + /// + protected abstract SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference reference, CancellationToken cancellationToken); private static TextSpan GetThisParameterDiagnosticSpan(ISymbol member) => member.Locations.First().SourceSpan; @@ -3370,7 +3515,7 @@ private static (SyntaxNode? Node, int Ordinal) GetParameterKey(IParameterSymbol if (containingLambda?.MethodKind == MethodKind.LambdaMethod || containingLambda?.MethodKind == MethodKind.LocalFunction) { - var oldContainingLambdaSyntax = GetSymbolSyntax(containingLambda, cancellationToken); + var oldContainingLambdaSyntax = containingLambda.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); return (oldContainingLambdaSyntax, parameter.Ordinal); } else @@ -3460,7 +3605,7 @@ private void CalculateCapturedVariablesMaps( } else { - oldLocalCapturesBySyntax.Add(GetSymbolSyntax(oldCapture, cancellationToken), i); + oldLocalCapturesBySyntax.Add(oldCapture.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken), i); } } @@ -3493,7 +3638,7 @@ private void CalculateCapturedVariablesMaps( } else { - var newCaptureSyntax = GetSymbolSyntax(newCapture, cancellationToken); + var newCaptureSyntax = newCapture.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); // variable doesn't exists in the old method or has not been captured prior the edit: if (!map.Reverse.TryGetValue(newCaptureSyntax, out var mappedOldSyntax) || @@ -3737,7 +3882,7 @@ private SyntaxNode GetCapturedVariableScope(ISymbol localOrParameter, SyntaxNode // lambda parameters and C# constructor parameters are lifted to their own scope: if ((member as IMethodSymbol)?.MethodKind == MethodKind.AnonymousFunction || HasParameterClosureScope(member)) { - var result = GetSymbolSyntax(localOrParameter, cancellationToken); + var result = localOrParameter.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); Debug.Assert(IsLambda(result)); return result; } @@ -3745,7 +3890,7 @@ private SyntaxNode GetCapturedVariableScope(ISymbol localOrParameter, SyntaxNode return memberBody; } - var node = GetSymbolSyntax(localOrParameter, cancellationToken); + var node = localOrParameter.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken); while (true) { RoslynDebug.Assert(node is object); @@ -3774,15 +3919,10 @@ private static bool AreEquivalentClosureScopes(SyntaxNode oldScopeOpt, SyntaxNod private void ReportStateMachineRudeEdits( Compilation oldCompilation, - UpdatedMemberInfo updatedInfo, ISymbol oldMember, + SyntaxNode newBody, ArrayBuilder diagnostics) { - if (!updatedInfo.OldHasStateMachineSuspensionPoint) - { - return; - } - // Only methods, local functions and anonymous functions can be async/iterators machines, // but don't assume so to be resiliant against errors in code. if (oldMember is not IMethodSymbol oldMethod) @@ -3808,8 +3948,8 @@ private void ReportStateMachineRudeEdits( { diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.UpdatingStateMachineMethodMissingAttribute, - GetBodyDiagnosticSpan(updatedInfo.NewBody, EditKind.Update), - updatedInfo.NewBody, + GetBodyDiagnosticSpan(newBody, EditKind.Update), + newBody, new[] { stateMachineAttributeQualifiedName })); } } @@ -3837,6 +3977,27 @@ private static bool TryGetTextSpan(TextLineCollection lines, LinePositionSpan li return true; } + internal static void AddNodes(ArrayBuilder nodes, SyntaxList list) + where T : SyntaxNode + { + foreach (var node in list) + { + nodes.Add(node); + } + } + + internal static void AddNodes(ArrayBuilder nodes, SeparatedSyntaxList? list) + where T : SyntaxNode + { + if (list.HasValue) + { + foreach (var node in list.Value) + { + nodes.Add(node); + } + } + } + #endregion #region Testing @@ -3851,22 +4012,7 @@ internal readonly struct TestAccessor public TestAccessor(AbstractEditAndContinueAnalyzer abstractEditAndContinueAnalyzer) => _abstractEditAndContinueAnalyzer = abstractEditAndContinueAnalyzer; - internal void AnalyzeMemberBodiesSyntax( - EditScript script, - Dictionary editMap, - SourceText oldText, - SourceText newText, - ImmutableArray oldActiveStatements, - ImmutableArray newActiveStatementSpans, - [Out] ImmutableArray.Builder newActiveStatements, - [Out] ImmutableArray>.Builder newExceptionRegions, - [Out] ArrayBuilder updatedMethods, - [Out] ArrayBuilder diagnostics) - { - _abstractEditAndContinueAnalyzer.AnalyzeMemberBodiesSyntax(script, editMap, oldText, newText, oldActiveStatements, newActiveStatementSpans, newActiveStatements, newExceptionRegions, updatedMethods, diagnostics); - } - - internal void ReportTopLevelSynctactiveRudeEdits(ArrayBuilder diagnostics, EditScript syntacticEdits, Dictionary editMap) + internal void ReportTopLevelSyntacticRudeEdits(ArrayBuilder diagnostics, EditScript syntacticEdits, Dictionary editMap) => _abstractEditAndContinueAnalyzer.ReportTopLevelSyntacticRudeEdits(diagnostics, syntacticEdits, editMap); internal void AnalyzeUnchangedDocument( @@ -3911,22 +4057,6 @@ internal void AnalyzeTrivia( { _abstractEditAndContinueAnalyzer.AnalyzeTrivia(oldSource, newSource, topMatch, editMap, triviaEdits, lineEdits, diagnostics, cancellationToken); } - - internal void AnalyzeSemantics( - EditScript editScript, - Dictionary editMap, - SourceText oldText, - ImmutableArray oldActiveStatements, - ArrayBuilder<(SyntaxNode OldNode, SyntaxNode NewNode)> triviaEdits, - ArrayBuilder updatedMembers, - SemanticModel oldModel, - SemanticModel newModel, - [Out] ArrayBuilder semanticEdits, - [Out] ArrayBuilder diagnostics, - CancellationToken cancellationToken) - { - _abstractEditAndContinueAnalyzer.AnalyzeSemantics(editScript, editMap, oldText, oldActiveStatements, triviaEdits, updatedMembers, oldModel, newModel, semanticEdits, diagnostics, cancellationToken); - } } #endregion diff --git a/src/Features/Core/Portable/EditAndContinue/DocumentAnalysisResults.cs b/src/Features/Core/Portable/EditAndContinue/DocumentAnalysisResults.cs index 88bb455a730ff..61c2cf775c9ef 100644 --- a/src/Features/Core/Portable/EditAndContinue/DocumentAnalysisResults.cs +++ b/src/Features/Core/Portable/EditAndContinue/DocumentAnalysisResults.cs @@ -36,7 +36,7 @@ internal sealed class DocumentAnalysisResults /// /// Edits made in the document, or null if the document is unchanged, has syntax errors or rude edits. /// - public ImmutableArray SemanticEdits { get; } + public ImmutableArray SemanticEdits { get; } /// /// Exception regions -- spans of catch and finally handlers that surround the active statements. @@ -83,7 +83,7 @@ public DocumentAnalysisResults( DocumentId documentId, ImmutableArray activeStatementsOpt, ImmutableArray rudeEdits, - ImmutableArray semanticEditsOpt, + ImmutableArray semanticEditsOpt, ImmutableArray> exceptionRegionsOpt, ImmutableArray lineEditsOpt, bool hasChanges, @@ -100,6 +100,8 @@ public DocumentAnalysisResults( } else if (hasChanges) { + Debug.Assert(!activeStatementsOpt.IsDefault); + if (rudeEdits.Length > 0) { Debug.Assert(semanticEditsOpt.IsDefault); @@ -108,7 +110,6 @@ public DocumentAnalysisResults( } else { - Debug.Assert(!activeStatementsOpt.IsDefault); Debug.Assert(!semanticEditsOpt.IsDefault); Debug.Assert(!exceptionRegionsOpt.IsDefault); Debug.Assert(!lineEditsOpt.IsDefault); @@ -145,34 +146,29 @@ public bool HasChangesAndSyntaxErrors public bool HasSignificantValidChanges => HasChanges && (!SemanticEdits.IsDefaultOrEmpty || !LineEdits.IsDefaultOrEmpty); - public static DocumentAnalysisResults SyntaxErrors(DocumentId documentId, bool hasChanges) + /// + /// Report errors blocking the document analysis. + /// + public static DocumentAnalysisResults SyntaxErrors(DocumentId documentId, ImmutableArray rudeEdits, bool hasChanges) => new( documentId, activeStatementsOpt: default, - rudeEdits: ImmutableArray.Empty, + rudeEdits: rudeEdits, semanticEditsOpt: default, exceptionRegionsOpt: default, lineEditsOpt: default, hasChanges, hasSyntaxErrors: true); - public static DocumentAnalysisResults Errors(DocumentId documentId, ImmutableArray activeStatementsOpt, ImmutableArray rudeEdits) - => new( - documentId, - activeStatementsOpt, - rudeEdits, - semanticEditsOpt: default, - exceptionRegionsOpt: default, - lineEditsOpt: default, - hasChanges: true, - hasSyntaxErrors: false); - + /// + /// Report unchanged document results. + /// public static DocumentAnalysisResults Unchanged(DocumentId documentId, ImmutableArray activeStatements, ImmutableArray> exceptionRegions) => new( documentId, activeStatements, rudeEdits: ImmutableArray.Empty, - semanticEditsOpt: ImmutableArray.Empty, + semanticEditsOpt: ImmutableArray.Empty, exceptionRegions, lineEditsOpt: ImmutableArray.Empty, hasChanges: false, diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index dd0b371fbfe99..bb0ccbebb5e0d 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -66,7 +66,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.InsertAroundActiveStatement, nameof(FeaturesResources.Adding_0_around_an_active_statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.DeleteAroundActiveStatement, nameof(FeaturesResources.Deleting_0_around_an_active_statement_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.DeleteActiveStatement, nameof(FeaturesResources.An_active_statement_has_been_removed_from_its_original_method_You_must_revert_your_changes_to_continue_or_restart_the_debugging_session)); + AddRudeEdit(RudeEditKind.DeleteActiveStatement, nameof(FeaturesResources.Removing_0_that_contains_an_active_statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdateAroundActiveStatement, nameof(FeaturesResources.Updating_a_0_around_an_active_statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdateExceptionHandlerOfActiveTry, nameof(FeaturesResources.Modifying_a_catch_finally_handler_with_an_active_statement_in_the_try_block_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdateTryOrCatchWithActiveFinally, nameof(FeaturesResources.Modifying_a_try_catch_finally_statement_when_the_finally_block_is_active_will_prevent_the_debug_session_from_continuing)); @@ -104,13 +104,12 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.GenericTypeUpdate, nameof(FeaturesResources.Modifying_a_method_inside_the_context_of_a_generic_type_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.GenericTypeTriviaUpdate, nameof(FeaturesResources.Modifying_whitespace_or_comments_in_0_inside_the_context_of_a_generic_type_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.GenericTypeInitializerUpdate, nameof(FeaturesResources.Modifying_the_initializer_of_0_in_a_generic_type_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.PartialTypeInitializerUpdate, nameof(FeaturesResources.Modifying_the_initializer_of_0_in_a_partial_type_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, nameof(FeaturesResources.Adding_a_constructor_to_a_type_with_a_field_or_property_initializer_that_contains_an_anonymous_function_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.RenamingCapturedVariable, nameof(FeaturesResources.Renaming_a_captured_variable_from_0_to_1_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.StackAllocUpdate, nameof(FeaturesResources.Modifying_0_which_contains_the_stackalloc_operator_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ExperimentalFeaturesEnabled, nameof(FeaturesResources.Modifying_source_with_experimental_language_features_enabled_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.AwaitStatementUpdate, nameof(FeaturesResources.Updating_a_complex_statement_containing_an_await_expression_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.ChangingConstructorVisibility, nameof(FeaturesResources.Changing_visibility_of_a_constructor_will_prevent_the_debug_session_from_continuing)); + AddRudeEdit(RudeEditKind.ChangingVisibility, nameof(FeaturesResources.Changing_visibility_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.CapturingVariable, nameof(FeaturesResources.Capturing_variable_0_that_hasn_t_been_captured_before_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.NotCapturingVariable, nameof(FeaturesResources.Ceasing_to_capture_variable_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.DeletingCapturedVariable, nameof(FeaturesResources.Deleting_captured_variable_0_will_prevent_the_debug_session_from_continuing)); @@ -127,7 +126,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.ActiveStatementLambdaRemoved, nameof(FeaturesResources.Removing_0_that_contains_an_active_statement_will_prevent_the_debug_session_from_continuing)); // TODO: change the error message to better explain what's going on AddRudeEdit(RudeEditKind.PartiallyExecutedActiveStatementUpdate, nameof(FeaturesResources.Updating_an_active_statement_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.PartiallyExecutedActiveStatementDelete, nameof(FeaturesResources.An_active_statement_has_been_removed_from_its_original_method_You_must_revert_your_changes_to_continue_or_restart_the_debugging_session)); + AddRudeEdit(RudeEditKind.PartiallyExecutedActiveStatementDelete, nameof(FeaturesResources.Removing_0_that_contains_an_active_statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.InsertFile, nameof(FeaturesResources.Adding_a_new_file_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdatingStateMachineMethodAroundActiveStatement, nameof(FeaturesResources.Updating_async_or_iterator_modifier_around_an_active_statement_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, nameof(FeaturesResources.Attribute_0_is_missing_Updating_an_async_method_or_an_iterator_will_prevent_the_debug_session_from_continuing)); @@ -144,6 +143,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.MemberBodyInternalError, nameof(FeaturesResources.Modifying_body_of_member_will_prevent_the_debug_session_from_continuing_due_to_internal_error)); AddRudeEdit(RudeEditKind.MemberBodyTooBig, nameof(FeaturesResources.Modifying_body_of_member_will_prevent_the_debug_session_from_continuing_because_the_body_has_too_many_statements)); AddRudeEdit(RudeEditKind.SourceFileTooBig, nameof(FeaturesResources.Modifying_source_file_will_prevent_the_debug_session_from_continuing_because_the_file_is_too_big)); + AddRudeEdit(RudeEditKind.InsertIntoGenericType, nameof(FeaturesResources.Adding_0_into_a_generic_type_will_prevent_the_debug_session_from_continuing)); // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_will_prevent_the_debug_session_from_continuing)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 0ffcef9df0721..411cce2984d1c 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -14,6 +14,7 @@ using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue; using Roslyn.Utilities; @@ -516,7 +517,7 @@ private static ProjectAnalysisSummary GetProjectAnalysisSymmary(ImmutableArray changedDocumentAnalyses) + private static ProjectChanges GetProjectChanges(Compilation newCompilation, ImmutableArray changedDocumentAnalyses, CancellationToken cancellationToken) { try { - using var _1 = ArrayBuilder.GetInstance(out var allEdits); + using var _1 = ArrayBuilder.GetInstance(out var allEdits); using var _2 = ArrayBuilder<(DocumentId, ImmutableArray)>.GetInstance(out var allLineEdits); using var _3 = ArrayBuilder<(DocumentId, ImmutableArray, ImmutableArray>)>.GetInstance(out var activeStatementsInChangedDocuments); - using var _4 = ArrayBuilder.GetInstance(out var allAddedSymbols); foreach (var analysis in changedDocumentAnalyses) { @@ -567,14 +567,6 @@ private static ProjectChanges GetProjectChanges(ImmutableArray 0) @@ -588,10 +580,12 @@ private static ProjectChanges GetProjectChanges(ImmutableArray edits, + out ImmutableArray mergedEdits, + out ImmutableHashSet addedSymbols, + CancellationToken cancellationToken) + { + using var _0 = ArrayBuilder.GetInstance(edits.Count, out var mergedEditsBuilder); + using var _1 = PooledHashSet.GetInstance(out var addedSymbolsBuilder); + using var _2 = ArrayBuilder.GetInstance(edits.Count, out var resolvedSymbols); + + foreach (var edit in edits) + { + var resolution = edit.Symbol.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken); + Contract.ThrowIfNull(resolution.Symbol); + resolvedSymbols.Add(resolution.Symbol); + } + + for (var i = 0; i < edits.Count; i++) + { + var edit = edits[i]; + + if (edit.PartialType == null) + { + var symbol = resolvedSymbols[i]; + + if (edit.Kind == SemanticEditKind.Insert) + { + // Inserts do not need partial type merging. + addedSymbolsBuilder.Add(symbol); + } + + mergedEditsBuilder.Add(new SemanticEdit( + edit.Kind, + oldSymbol: null, + newSymbol: symbol, + syntaxMap: edit.SyntaxMap, + preserveLocalVariables: edit.SyntaxMap != null)); + } + } + + // no partial type merging needed: + if (edits.Count == mergedEditsBuilder.Count) + { + mergedEdits = mergedEditsBuilder.ToImmutable(); + addedSymbols = addedSymbolsBuilder.ToImmutableHashSet(); + return; + } + + // Calculate merged syntax map for each partial type symbol: + + var symbolKeyComparer = SymbolKey.GetComparer(ignoreCase: false, ignoreAssemblyKeys: true); + var mergedSyntaxMaps = new Dictionary>(symbolKeyComparer); + + foreach (var partialTypeEdits in edits.Where(edit => edit.PartialType != null).GroupBy(edit => edit.PartialType!.Value, symbolKeyComparer)) + { + // the symbols may only be constructors, all of which have a single syntax declaration + Debug.Assert(partialTypeEdits.Select((edit, index) => resolvedSymbols[index]).All(symbol => symbol is IMethodSymbol + { + MethodKind: MethodKind.Constructor or MethodKind.StaticConstructor or MethodKind.SharedConstructor, + DeclaringSyntaxReferences: { Length: 1 } + })); + + Debug.Assert(partialTypeEdits.All(edit => edit.SyntaxMap != null)); + + var trees = partialTypeEdits.Select((edit, index) => resolvedSymbols[index].DeclaringSyntaxReferences.Single().SyntaxTree).ToImmutableArray(); + var syntaxMaps = partialTypeEdits.SelectAsArray(edit => edit.SyntaxMap!); + + mergedSyntaxMaps.Add(partialTypeEdits.Key, node => syntaxMaps[trees.IndexOf(node.SyntaxTree)](node)); + } + + // Deduplicate updates based on new symbol and use merged syntax map calculated above for a given partial type. + + using var _3 = PooledHashSet.GetInstance(out var visitedSymbols); + + for (var i = 0; i < edits.Count; i++) + { + var edit = edits[i]; + + if (edit.PartialType != null) + { + Contract.ThrowIfFalse(edit.Kind == SemanticEditKind.Update); + + var newSymbol = resolvedSymbols[i]; + if (visitedSymbols.Add(newSymbol)) + { + mergedEditsBuilder.Add(new SemanticEdit(SemanticEditKind.Update, oldSymbol: null, newSymbol, mergedSyntaxMaps[edit.PartialType.Value], preserveLocalVariables: true)); + } + } + } + + mergedEdits = mergedEditsBuilder.ToImmutable(); + addedSymbols = addedSymbolsBuilder.ToImmutableHashSet(); + } + public async Task EmitSolutionUpdateAsync(Solution solution, SolutionActiveStatementSpanProvider solutionActiveStatementSpanProvider, CancellationToken cancellationToken) { try @@ -711,7 +800,7 @@ public async Task EmitSolutionUpdateAsync(Solution solution, Sol var currentCompilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); Contract.ThrowIfNull(currentCompilation); - var projectChanges = GetProjectChanges(changedDocumentAnalyses); + var projectChanges = GetProjectChanges(currentCompilation, changedDocumentAnalyses, cancellationToken); var baseActiveStatements = await BaseActiveStatements.GetValueAsync(cancellationToken).ConfigureAwait(false); // Exception regions of active statements in changed documents are calculated (non-default), diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 065ad8bcfc229..c8a5d18c294ad 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -54,7 +54,7 @@ internal enum RudeEditKind : ushort GenericTypeUpdate = 38, GenericTypeTriviaUpdate = 39, GenericTypeInitializerUpdate = 40, - PartialTypeInitializerUpdate = 41, + // PartialTypeInitializerUpdate = 41, // AsyncMethodUpdate = 42, // AsyncMethodTriviaUpdate = 43, StackAllocUpdate = 44, @@ -62,7 +62,7 @@ internal enum RudeEditKind : ushort ExperimentalFeaturesEnabled = 45, AwaitStatementUpdate = 46, - ChangingConstructorVisibility = 47, + ChangingVisibility = 47, CapturingVariable = 48, NotCapturingVariable = 49, @@ -116,5 +116,6 @@ internal enum RudeEditKind : ushort MemberBodyInternalError = 88, SourceFileTooBig = 89, MemberBodyTooBig = 90, + InsertIntoGenericType = 91, } } diff --git a/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs b/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs new file mode 100644 index 0000000000000..2663acf1067ce --- /dev/null +++ b/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs @@ -0,0 +1,49 @@ +// 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. + +using System; +using Microsoft.CodeAnalysis.Emit; + +namespace Microsoft.CodeAnalysis.EditAndContinue +{ + internal readonly struct SemanticEditInfo + { + /// + /// or . + /// + public SemanticEditKind Kind { get; } + + /// + /// If is represents the inserted symbol in the new compilation. + /// If is represents the updated symbol in both compilations. + /// + /// We use to represent the symbol rather then , + /// since different semantic edits might hav ebeen calculated against different solution snapshot and thus symbols are not directly comparable. + /// When the edits are processed we map the to the current compilation. + /// + public SymbolKey Symbol { get; } + + public Func? SyntaxMap { get; } + + /// + /// Specified if the edit needs to be merged with other edits of the same . + /// for edits of non-partial types or their members and of a member of a partial type that do not require merging. + /// + /// If specified, the is incomplete: it only provides mapping of the changed members of a single partial type declaration. + /// + public SymbolKey? PartialType { get; } + + public SemanticEditInfo( + SemanticEditKind kind, + SymbolKey symbol, + Func? syntaxMap, + SymbolKey? partialType) + { + Kind = kind; + Symbol = symbol; + SyntaxMap = syntaxMap; + PartialType = partialType; + } + } +} diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 3310bdbcd4df1..755299062dae6 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -363,8 +363,8 @@ Updating a complex statement containing an await expression will prevent the debug session from continuing. - - Changing visibility of a constructor will prevent the debug session from continuing. + + Changing visibility of {0} will prevent the debug session from continuing. Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. @@ -408,6 +408,9 @@ Adding '{0}' into an interface will prevent the debug session from continuing. + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into an interface method will prevent the debug session from continuing. @@ -496,7 +499,7 @@ Moving '{0}' will prevent the debug session from continuing. - Deleting '{0}' will prevent the debug session from continuing. + Deleting {0} will prevent the debug session from continuing. Deleting '{0}' around an active statement will prevent the debug session from continuing. @@ -535,9 +538,6 @@ Modifying the initializer of '{0}' in a generic type will prevent the debug session from continuing. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. @@ -572,7 +572,7 @@ Updating an active statement will prevent the debug session from continuing. - Removing '{0}' that contains an active statement will prevent the debug session from continuing. + Removing {0} that contains an active statement will prevent the debug session from continuing. Adding a new file will prevent the debug session from continuing. @@ -791,6 +791,9 @@ Do you want to continue? constructor + + static constructor + auto-property @@ -2805,6 +2808,13 @@ Zero-width positive lookbehind assertions are typically used at the beginning of {0} - {1} + + {0} '{1}' + e.g. "method 'M'" + + + code + Convert to record diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index a4e4256d2e0c7..5c6a209574021 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -90,6 +90,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Přidat název elementu řazené kolekce členů {0} + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Přidání prvku {0} do metody rozhraní zabrání v pokračování relace ladění. @@ -125,6 +130,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Podmínky alternativního výrazu nezachytávají a nejde je pojmenovat. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' Očekávaná úloha vrací {0}. @@ -205,6 +215,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Pokud se změní {0} na {1}, relace ladění nebude moct pokračovat, protože dojde ke změně tvaru stavového počítače. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Nakonfigurovat styl kódu {0} @@ -2336,6 +2351,11 @@ Pokud se specifikátor formátu H použije bez dalších specifikátorů vlastn Specifikátor vlastního formátu HH reprezentuje hodinu jako číslo od 00 do 23. To znamená, že hodina je reprezentována ve 24hodinovém formátu, který začíná na nule a počítá hodiny od půlnoci. Hodina s jednou číslicí se formátuje s nulou na začátku. + + code + code + + date separator oddělovač data @@ -2492,6 +2512,11 @@ Specifikátor standardního formátu f představuje kombinaci vzorů dlouhého d Specifikátor standardního formátu T představuje řetězec vlastního formátu data a času, který se definuje pomocí vlastnosti DateTimeFormatInfo.LongTimePattern konkrétní jazykové verze. Například řetězec vlastního formátu pro invariantní jazykovou verzi je HH:mm:ss. + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) minuta (1–2 číslice) @@ -2944,11 +2969,6 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn Aktualizace komplexního příkazu, který obsahuje výraz await, znemožní relaci ladění pokračovat. - - Changing visibility of a constructor will prevent the debug session from continuing. - Změna viditelnosti konstruktoru znemožní relaci ladění pokračovat. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Zachycení proměnné {0}, která se nezachytila dřív, zabrání v pokračování relace ladění. @@ -3155,8 +3175,8 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn - Deleting '{0}' will prevent the debug session from continuing. - Odstranění prvku {0} zabrání v pokračování relace ladění. + Deleting {0} will prevent the debug session from continuing. + Odstranění prvku {0} zabrání v pokračování relace ladění. @@ -3174,11 +3194,6 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn Odstranění těla metody zabrání v pokračování relace ladění. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Aktivní příkaz je odstraněný z jeho původní metody. Pro pokračování musíte vzít zpátky změny nebo restartovat ladicí relaci. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Aktualizace modifikátoru async nebo iterator kolem aktivního příkazu bude bránit relaci ladění v tom, aby pokračovala. @@ -3209,11 +3224,6 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn Modifikace inicializátoru pro {0} v obecném typu zabrání v pokračování relace ladění. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Modifikace inicializátoru pro {0} v částečném typu zabrání v pokračování relace ladění. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Přidáním konstruktoru do typu s inicializátorem pole nebo vlastnosti obsahujícím anonymní funkci zabráníte ladicí relaci v pokračování. @@ -3265,8 +3275,8 @@ Pokud se specifikátor formátu g použije bez dalších specifikátorů vlastn - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Odebrání prvku {0}, který obsahuje aktivní příkaz, zabrání v pokračování relace ladění. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Odebrání prvku {0}, který obsahuje aktivní příkaz, zabrání v pokračování relace ladění. @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation Když se tento specifikátor standardního formátu použije, operace formátování nebo parsování vždy použije invariantní jazykovou verzi. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'Symbol nemůže být obor názvů. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index ce77750806a5e..b5ce297a5a12b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -90,6 +90,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Tupelelementnamen "{0}" hinzufügen + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Das Hinzufügen von "{0}" zu einer Schnittstellenmethode verhindert das Fortsetzen der Debugsitzung. @@ -125,6 +130,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Wechselbedingungen werden nicht erfasst und können nicht benannt werden. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' Erwartete Aufgabe gibt "{0}" zurück @@ -205,6 +215,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Die Änderung von "{0}" in "{1}" verhindert, dass die Debugsitzung fortgesetzt wird, weil sich dadurch die Form des Zustandsautomaten verändert. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Codeformat "{0}" konfigurieren @@ -2336,6 +2351,11 @@ Bei Verwendung des Formatbezeichners "H" ohne weitere benutzerdefinierte Formatb Der benutzerdefinierte Formatbezeichner "HH" (plus beliebig viele zusätzliche H-Bezeichner) repräsentiert die Stunde als eine Zahl zwischen 00 und 23, d. h., die Stunde wird in einem nullbasierten 24-Stunden-Format dargestellt, bei dem die Stunden seit Mitternacht gezählt werden. Ein einstelliger Stundenwert wird mit einer führenden Null formatiert. + + code + code + + date separator Datumstrennzeichen @@ -2492,6 +2512,11 @@ Der Standardformatbezeichner "f" repräsentiert eine Kombination aus den Mustern Der Standardformatbezeichner "T" repräsentiert eine benutzerdefinierte Datums- und Uhrzeitformatzeichenfolge, die durch die DateTimeFormatInfo.LongTimePattern-Eigenschaft einer bestimmten Kultur definiert ist. Die benutzerdefinierte Formatzeichenfolge für die invariante Kultur lautet beispielsweise "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) Minute (1–2 Stellen) @@ -2944,11 +2969,6 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb Beim Ändern von komplexen Anweisungen, die einen "await"-Ausdruck enthalten, wird die Debuggingsitzung unterbrochen. - - Changing visibility of a constructor will prevent the debug session from continuing. - Bei Änderungen an der Sichtbarkeit von Konstruktoren wird die Debuggingsitzung unterbrochen. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Nach dem Erfassen der zuvor noch nicht erfassten Variable "{0}" kann die Debugsitzung nicht mehr fortgesetzt werden. @@ -3155,8 +3175,8 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb - Deleting '{0}' will prevent the debug session from continuing. - Durch Löschen von "{0}" wird verhindert, dass die Debuggingsitzung fortgesetzt wird. + Deleting {0} will prevent the debug session from continuing. + Durch Löschen von "{0}" wird verhindert, dass die Debuggingsitzung fortgesetzt wird. @@ -3174,11 +3194,6 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb Durch Löschen eines Methodenkörpers wird verhindert, dass eine Debuggingsitzung fortgesetzt wird. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Eine aktive Anweisung wurde aus der ursprünglichen Methode entfernt. Sie müssen Ihre Änderungen rückgängig machen, um die Debuggingsitzung fortzusetzen oder neu zu starten. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Eine asynchrone Aktualisierung oder ein iteratormodifizierer um eine aktive Anweisung verhindert, dass die Debugsitzung fortgesetzt wird. @@ -3209,11 +3224,6 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb Durch Ändern des Initialisierers von "{0}" in einem generischen Typen wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Durch Ändern des Initialisierers von "{0}" in einem partiellen Typ wird verhindert, dass die Debuggingsitzung fortgesetzt wird. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Das Hinzufügen eines Konstruktors zu einem Typ mit einem Feld- oder Eigenschafteninitialisierer, der eine anonyme Funktion enthält, verhindert das Fortsetzen der Debugsitzung. @@ -3265,8 +3275,8 @@ Bei Verwendung des Formatbezeichners "g" ohne weitere benutzerdefinierte Formatb - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Durch Entfernen von "{0}", das eine aktive Anweisung enthält, wird verhindert, dass die Debuggingsitzung fortgesetzt wird. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Durch Entfernen von "{0}", das eine aktive Anweisung enthält, wird verhindert, dass die Debuggingsitzung fortgesetzt wird. @@ -3660,6 +3670,11 @@ Der Zweck des Formatbezeichners "s" besteht darin, Ergebniszeichenfolgen zu erze Bei Verwendung dieses Standardformatbezeichners wird zur Formatierung oder Analyse immer die invariante Kultur verwendet. + + static constructor + static constructor + + 'symbol' cannot be a namespace. '"symbol" kann kein Namespace sein. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 02f324b293deb..e626a716f03db 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -90,6 +90,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Agregar el nombre del elemento de tupla "{0}" + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Agregar "{0}" a un método de interfaz impedirá que continúe la sesión de depuración. @@ -125,6 +130,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Las condiciones de alternancia no se captan y se les puede poner un nombre This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' La tarea esperada devuelve "{0}". @@ -205,6 +215,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Si se cambia "{0}" a "{1}", la sesión de depuración no podrá continuar porque cambia la forma de la máquina de estados. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Configurar el estilo de código de {0} @@ -2336,6 +2351,11 @@ Si el especificador de formato "H" se usa sin otros especificadores de formato p El especificador de formato personalizado "HH" (más cualquier número de especificadores "H" adicionales) representa la hora como un número de 00 a 23, es decir, mediante un reloj de 24 horas de base cero que cuenta las horas desde la medianoche. El formato de una hora de un solo dígito es con un cero inicial. + + code + code + + date separator separador de fecha @@ -2492,6 +2512,11 @@ El especificador de formato estándar "f" representa una combinación de los pat El especificador de formato estándar "T" representa una cadena de formato de fecha y hora personalizado que está definida por la propiedad DateTimeFormatInfo.LongTimePattern de una referencia cultural específica. Por ejemplo, la cadena de formato personalizado para la referencia cultural invariable es "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) minuto (1-2 dígitos) @@ -2944,11 +2969,6 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p Si se actualiza una instrucción compleja que contiene una expresión await, la sesión de depuración no podrá continuar. - - Changing visibility of a constructor will prevent the debug session from continuing. - Si se cambia la visibilidad de un constructor, la sesión de depuración no podrá continuar. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. La captura de una variable "{0}" que no se había capturado antes impedirá que continúe la sesión de depuración. @@ -3155,8 +3175,8 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p - Deleting '{0}' will prevent the debug session from continuing. - Eliminar '{0}' impedirá que continúe la sesión de depuración. + Deleting {0} will prevent the debug session from continuing. + Eliminar '{0}' impedirá que continúe la sesión de depuración. @@ -3174,11 +3194,6 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p Eliminar un cuerpo de método impedirá que continúe la sesión de depuración. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Se quitó una instrucción activa de su método original. Debe revertir los cambios para continuar o reiniciar la sesión de depuración. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. La actualización del modificador async o iterator en una instrucción activa impedirá que la sesión de depuración continúe. @@ -3209,11 +3224,6 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p Modificar el inicializador de '{0}' en un tipo genérico impedirá que continúe la sesión de depuración. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Modificar el inicializador de '{0}' en un tipo parcial impedirá que continúe la sesión de depuración. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Añadir un constructor a un tipo con un inicializador de campo o propiedad que contiene una función anónima detendrá la sesión de depuración. @@ -3265,8 +3275,8 @@ Si el especificador de formato "g" se usa sin otros especificadores de formato p - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Quitar '{0}' que contiene una instrucción activa impedirá que continúe la sesión de depuración. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Quitar '{0}' que contiene una instrucción activa impedirá que continúe la sesión de depuración. @@ -3660,6 +3670,11 @@ La finalidad del especificador de formato "s" es producir cadenas de resultados Cuando se usa este especificador de formato estándar, la operación de formato o análisis utiliza siempre la referencia cultural invariable. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'símbolo' no puede ser un espacio de nombres. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index afcff34a0a291..5cfc63f66348f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -90,6 +90,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Ajouter le nom d'élément tuple '{0}' + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. L'ajout de '{0}' à une méthode d'interface empêche la session de débogage de se poursuivre. @@ -125,6 +130,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Les conditions d'alternance n'effectuent pas de capture et ne peuvent pas être nommées This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' La tâche attendue retourne '{0}' @@ -205,6 +215,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai Le remplacement de '{0}' par '{1}' va empêcher la session de débogage de se poursuivre, car cela change la forme de la machine à états. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Configurer le style de code {0} @@ -2336,6 +2351,11 @@ Si le spécificateur de format "H" est utilisé sans autres spécificateurs de f Le spécificateur de format personnalisé "HH" (plus n'importe quel nombre de spécificateurs "H" supplémentaires) représente les heures sous la forme d'un nombre compris entre 00 et 23. En d'autres termes, les heures sont représentées par une horloge de 24 heures de base zéro, qui compte les heures écoulées depuis minuit. Une heure à un chiffre est présentée dans un format qui comporte un zéro de début. + + code + code + + date separator séparateur de date @@ -2492,6 +2512,11 @@ Le spécificateur de format standard "f" représente une combinaison des modèle Le spécificateur de format standard "T" représente une chaîne de format de date et d'heure personnalisée, qui est définie par la propriété DateTimeFormatInfo.LongTimePattern d'une culture spécifique. Par exemple, la chaîne de format personnalisée pour la culture invariante est "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) minute (1 à 2 chiffres) @@ -2944,11 +2969,6 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f La mise à jour d'une instruction complexe contenant une expression await empêche la session de débogage de se poursuivre. - - Changing visibility of a constructor will prevent the debug session from continuing. - Le changement de visibilité d'un constructeur empêche la session de débogage de se poursuivre. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. La capture de la variable '{0}' qui n'a pas déjà été capturée empêche la session de débogage de se poursuivre. @@ -3155,8 +3175,8 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f - Deleting '{0}' will prevent the debug session from continuing. - La suppression de '{0}' empêche la session de débogage de se poursuivre. + Deleting {0} will prevent the debug session from continuing. + La suppression de '{0}' empêche la session de débogage de se poursuivre. @@ -3174,11 +3194,6 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f La suppression d'un corps de méthode empêche la session de débogage de se poursuivre. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Une instruction active a été supprimée de sa méthode d'origine. Annulez vos modifications si vous voulez continuer ou redémarrez la session de débogage. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. La mise à jour d'un modificateur async ou iterator autour d'une instruction active empêche la session de débogage de se poursuivre. @@ -3209,11 +3224,6 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f La modification de l'initialiseur de '{0}' dans un type générique empêche la session de débogage de se poursuivre. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - La modification de l'initialiseur de '{0}' dans un type partiel empêche la session de débogage de se poursuivre. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. L'ajout d'un constructeur à un type avec un initialiseur de champ ou de propriété contenant une fonction anonyme empêche la poursuite de la session de débogage. @@ -3265,8 +3275,8 @@ Si le spécificateur de format "g" est utilisé sans autres spécificateurs de f - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - La suppression de '{0}' contenant une instruction active empêche la session de débogage de se poursuivre. + Removing {0} that contains an active statement will prevent the debug session from continuing. + La suppression de '{0}' contenant une instruction active empêche la session de débogage de se poursuivre. @@ -3660,6 +3670,11 @@ Le spécificateur de format "s" a pour but de produire des chaînes triées de m Quand ce spécificateur de format standard est utilisé, l'opération qui consiste à appliquer un format ou à exécuter une analyse utilise toujours la culture invariante. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'symbol' ne peut pas être un espace de noms. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index c6acb049831bc..f58fc1b2fd4a8 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -90,6 +90,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Aggiungi il nome dell'elemento tupla '{0}' + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Se si aggiunge '{0}' in un metodo di interfaccia, la sessione di debug non potrà continuare. @@ -125,6 +130,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Le condizioni di alternanza non consentono l'acquisizione e non possono essere denominate This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' L'attività attesa restituisce '{0}' @@ -205,6 +215,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si modifica '{0}' in '{1}', la sessione di debug non potrà continuare perché modifica la forma della macchina a stati. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Configura lo stile del codice di {0} @@ -2336,6 +2351,11 @@ Se l'identificatore di formato "H" viene usato senza altri identificatori di for L'identificatore di formato personalizzato "HH" (più qualsiasi numero di identificatori "H" aggiuntivi) rappresenta l'ora come numero compreso tra 00 e 23, ovvero l'ora rappresentata nell'orario in formato 24 ore a base zero in base al quale il conteggio riparte da mezzanotte. Un'ora costituita da una singola cifra viene formattata con uno zero iniziale. + + code + code + + date separator Separatore di data @@ -2492,6 +2512,11 @@ L'identificatore di formato standard "f" rappresenta una combinazione degli sche L'identificatore di formato standard "T" rappresenta una stringa di formato di data e ora personalizzata definita dalla proprietà DateTimeFormatInfo.LongTimePattern di impostazioni cultura specifiche. Ad esempio, la stringa di formato personalizzata per le impostazioni cultura inglese non dipendenti da paese/area geografica è "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) Minuto (1-2 cifre) @@ -2944,11 +2969,6 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for Se si aggiorna un'istruzione complessa contenente un'espressione await, la sessione di debug non potrà continuare. - - Changing visibility of a constructor will prevent the debug session from continuing. - Se si modifica la visibilità di un costruttore, la sessione di debug non potrà continuare. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Se si acquisisce la variabile '{0}' che non è stata ancora acquisita, la sessione di debug non potrà continuare. @@ -3155,8 +3175,8 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for - Deleting '{0}' will prevent the debug session from continuing. - Se si elimina '{0}', la sessione di debug non potrà continuare. + Deleting {0} will prevent the debug session from continuing. + Se si elimina '{0}', la sessione di debug non potrà continuare. @@ -3174,11 +3194,6 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for Se si elimina un corpo del metodo, la sessione di debug non potrà continuare. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Un'istruzione attiva è stata rimossa dal metodo originale. Annullare le modifiche per continuare oppure riavviare la sessione di debug. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Se si aggiorna un modificatore iterator o async in prossimità di un'istruzione attiva, la sessione di debug non potrà continuare. @@ -3209,11 +3224,6 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for Se si modifica l'inizializzatore di '{0}' in un tipo generico, la sessione di debug non potrà continuare. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Se si modifica l'inizializzatore di '{0}' in un tipo parziale, la sessione di debug non potrà continuare. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Se aggiunge un costruttore a un tipo con un inizializzatore di campo o proprietà che contiene una funzione anonima, la sessione di debug non potrà continuare. @@ -3265,8 +3275,8 @@ Se l'identificatore di formato "g" viene usato senza altri identificatori di for - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Se si rimuove '{0}', che contiene un'istruzione attiva, la sessione di debug non potrà continuare. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Se si rimuove '{0}', che contiene un'istruzione attiva, la sessione di debug non potrà continuare. @@ -3660,6 +3670,11 @@ L'identificatore di formato "s" ha lo scopo di produrre stringhe di risultati or Quando si usa questo identificatore di formato standard, la formattazione o l'operazione di analisi usa sempre le impostazioni cultura inglese non dipendenti da paese/area geografica. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'L'elemento 'symbol' non può essere uno spazio dei nomi. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 19d0d6368d0ec..725ec5181ffbb 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -90,6 +90,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma タプル要素名 '{0}' を追加します + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. '{0}' をインターフェイス メソッドに追加すると、デバッグ セッションは続行されません。 @@ -125,6 +130,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 選択条件が捕捉されないため、名前を指定できません This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' 待機中のタスクから '{0}' が返されました @@ -205,6 +215,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}' から '{1}' に変更すると、ステート マシンの形状が変わるため、デバッグ セッションは続行されません。 + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style {0} コード スタイルの構成 @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's "HH" カスタム書式指定子 (任意の数の "H" 指定子を追加できます) は、時を 00 から 23 の数字で表します。つまり、午前 0 時からの時間をカウントする、ゼロから始まる 24 時間制で時が表されます。1 桁の時は、先行ゼロ付きで書式設定されます。 + + code + code + + date separator 日付の区切り記号 @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" "T" 標準書式指定子は、特定のカルチャの DateTimeFormatInfo.LongTimePattern プロパティで定義されているカスタム日時書式指定文字列を表します。たとえば、インバリアント カルチャのカスタム書式指定文字列は "HH:mm:ss" です。 + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) 分 (1 - 2 桁) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Await 式を含む複雑なステートメントを更新すると、デバッグ セッションを続行できなくなります。 - - Changing visibility of a constructor will prevent the debug session from continuing. - コンストラクターの表示範囲を変更すると、デバッグ セッションを続行できなくなります。 - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. 以前にキャプチャされたことのない変数 '{0}' をキャプチャすると、デバッグ セッションは続行されません。 @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - '{0}' を削除すると、デバッグ セッションは続行されません。 + Deleting {0} will prevent the debug session from continuing. + '{0}' を削除すると、デバッグ セッションは続行されません。 @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's メソッド本体を削除すると、デバッグ セッションは続行されません。 - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - アクティブ ステートメントは元のメソッドから削除されました。変更を元に戻して続行するか、またはデバッグ セッションを再開してください。 - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. アクティブ ステートメントの前後の async 修飾子または iterator 修飾子を更新すると、デバッグ セッションが継続しなくなります。 @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's ジェネリック型の '{0}' の初期化子を変更すると、デバッグ セッションは続行されません。 - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - 部分型の '{0}' の初期化子を変更すると、デバッグ セッションは続行されません。 - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. 匿名関数を含むフィールドまたはプロパティの初期化子を持つ型にコンストラクターを追加すると、デバッグ セッションを続行できなくなります。 @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - アクティブ ステートメンを含む '{0}' を削除すると、デバッグ セッションは続行されません。 + Removing {0} that contains an active statement will prevent the debug session from continuing. + アクティブ ステートメンを含む '{0}' を削除すると、デバッグ セッションは続行されません。 @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation この標準書式指定子が使用されている場合、書式設定操作または解析操作では常にインバリアント カルチャが使用されます。 + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'symbol' は名前空間にすることはできません。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index b4ed0092e732d..b36e3998c6f14 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -90,6 +90,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 튜플 요소 이름 '{0}' 추가 + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. 인터페이스 메서드에 '{0}'을(를) 추가하면 디버그 세션을 계속할 수 없습니다. @@ -125,6 +130,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 교체 조건은 캡처하지 않고 이름을 지정할 수 없습니다. This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' 대기된 작업에서 '{0}'이(가) 반환됨 @@ -205,6 +215,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma '{0}'을(를) '{1}'(으)로 변경하면 상태 시스템의 셰이프가 변경되므로 디버그 세션을 계속할 수 없습니다. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style {0} 코드 스타일 구성 @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's "HH" 사용자 지정 형식 지정자(및 임의 개수의 추가 "H" 지정자)는 시간을 00부터 23까지의 숫자로 나타냅니다. 즉, 시간은 자정부터 경과한 시간을 계산하는 0 기반 24시간제로 표현됩니다. 한 자릿수 시간은 앞에 0이 있는 형식으로 지정됩니다. + + code + code + + date separator 날짜 구분 기호 @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" "T" 표준 형식 지정자는 특정 문화권의 DateTimeFormatInfo.LongTimePattern 속성으로 정의되는 사용자 지정 날짜 및 시간 형식 문자열을 나타냅니다. 예를 들어, 고정 문화권의 사용자 지정 형식 문자열은 "HH:mm:ss"입니다. + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) 분(1~2자리) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's await 식이 포함된 복합 문을 업데이트하면 디버그 세션을 계속할 수 없습니다. - - Changing visibility of a constructor will prevent the debug session from continuing. - 생성자 표시 여부를 변경하면 디버그 세션을 계속할 수 없습니다. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. 이전에 캡처되지 않았던 '{0}' 변수를 캡처하면 디버그 세션을 계속할 수 없습니다. @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - '{0}'을(를) 삭제하면 디버그 세션을 계속할 수 없습니다. + Deleting {0} will prevent the debug session from continuing. + '{0}'을(를) 삭제하면 디버그 세션을 계속할 수 없습니다. @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 메서드 본문을 삭제하면 디버그 세션을 계속할 수 없습니다. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - 원래 메서드에서 활성 문이 제거되었습니다. 변경 내용을 취소하고 계속하거나 디버깅 세션을 다시 시작해야 합니다. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. 활성 문 주위의 async 또는 iterator 한정자를 업데이트하면 디버그 세션이 계속되지 않습니다. @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 제네릭 형식에서 '{0}'의 이니셜라이저를 수정하면 디버그 세션을 계속할 수 없습니다. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - 부분 형식에서 '{0}'의 이니셜라이저를 수정하면 디버그 세션을 계속할 수 없습니다. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. 익명 함수를 포함하는 필드 또는 속성 이니셜라이저가 있는 형식에 생성자를 추가하면 디버그 세션을 계속할 수 없습니다. @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - 활성 문이 포함된 '{0}'을(를) 제거하면 디버그 세션을 계속할 수 없습니다. + Removing {0} that contains an active statement will prevent the debug session from continuing. + 활성 문이 포함된 '{0}'을(를) 제거하면 디버그 세션을 계속할 수 없습니다. @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation 이 표준 형식 지정자를 사용하면 형식 지정 또는 구문 분석 연산에서 항상 고정 문화권이 사용됩니다. + + static constructor + static constructor + + 'symbol' cannot be a namespace. '기호'는 네임스페이스일 수 없습니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 2b2ea5d38aa33..c65d7f325db43 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -90,6 +90,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Dodaj nazwę elementu krotki „{0}” + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Dodanie elementu „{0}” do metody interfejsu uniemożliwi kontynuowanie sesji debugowania. @@ -125,6 +130,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Warunki alternatywne nie przechwytują i nie mogą być nazywane This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' Zadanie, na które oczekiwano, zwraca wartość „{0}” @@ -205,6 +215,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Zmiana elementu „{0}” na „{1}” uniemożliwi kontynuowanie sesji debugowania, ponieważ powoduje zmianę kształtu automatu stanów. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Konfiguruj styl kodu {0} @@ -2336,6 +2351,11 @@ Jeśli specyfikator formatu „H” zostanie użyty bez innych indywidualnych sp Indywidualny specyfikator formatu „HH” (wraz z dowolną liczbą dodatkowych specyfikatorów „H”) reprezentuje godzinę jako liczbę z zakresu od 00 do 23; tzn. godzina jest reprezentowana przez 24-godzinny zegar zaczynający się od zera i liczący godziny od północy. Godzina 1-cyfrowa jest wyświetlana w formacie z wiodącym zerem. + + code + code + + date separator separator daty @@ -2492,6 +2512,11 @@ Standardowy specyfikator formatu „f” reprezentuje połączenie wzorców dłu Standardowy specyfikator formatu „T” reprezentuje niestandardowy ciąg formatu daty i godziny zdefiniowany przez właściwość DateTimeFormatInfo.LongTimePattern konkretnej kultury. Na przykład niestandardowy ciąg formatu dla kultury niezmiennej to „HH:mm:ss”. + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) minuta (1-2 cyfry) @@ -2944,11 +2969,6 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp Zaktualizowanie instrukcji złożonej zawierającej wyrażenie await uniemożliwi kontynuowanie sesji debugowania. - - Changing visibility of a constructor will prevent the debug session from continuing. - Zmiana widoczności konstruktora uniemożliwi kontynuowanie sesji debugowania. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Przechwycenie zmiennej „{0}”, która nie została przechwycona wcześniej, uniemożliwi kontynuowanie sesji debugowania. @@ -3155,8 +3175,8 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp - Deleting '{0}' will prevent the debug session from continuing. - Usunięcie elementu „{0}” uniemożliwi kontynuowanie sesji debugowania. + Deleting {0} will prevent the debug session from continuing. + Usunięcie elementu „{0}” uniemożliwi kontynuowanie sesji debugowania. @@ -3174,11 +3194,6 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp Usunięcie treści metody uniemożliwi kontynuowanie sesji debugowania. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Aktywna instrukcja została usunięta ze swojej oryginalnej metody. Musisz cofnąć zmiany, aby kontynuować, lub uruchomić ponownie sesję debugowania. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Aktualizowanie modyfikatora asynchronicznego lub powiązanego z iteratorem w pobliżu aktywnej instrukcji uniemożliwi kontynuowanie sesji debugowania. @@ -3209,11 +3224,6 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp Modyfikacja inicjatora elementu „{0}” w typie ogólnym uniemożliwi kontynuowanie sesji debugowania. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Modyfikacja inicjatora elementu „{0}” w typie częściowym uniemożliwi kontynuowanie sesji debugowania. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Dodanie konstruktora do typu z inicjatorem pola lub właściwości zawierającego funkcję anonimową uniemożliwi kontynuowanie sesji debugowania. @@ -3265,8 +3275,8 @@ Jeśli specyfikator formatu „g” jest używany bez innych niestandardowych sp - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Usunięcie elementu „{0}” zawierającego aktywną instrukcję uniemożliwi kontynuowanie sesji debugowania. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Usunięcie elementu „{0}” zawierającego aktywną instrukcję uniemożliwi kontynuowanie sesji debugowania. @@ -3660,6 +3670,11 @@ Specyfikator formatu „s” jest przeznaczony do tworzenia ciągów wyniku, kt Gdy jest używany ten standardowy specyfikator formatu, operacja formatowania lub analizowania zawsze używa kultury niezmiennej. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'Element „symbol” nie może być przestrzenią nazw. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 39dab409b6bfe..af5616b188b04 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -90,6 +90,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Adicionar o nome do elemento de tupla '{0}' + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Adicionar '{0}' a um método de interface impedirá que a sessão de depuração continue. @@ -125,6 +130,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Condições de alternância não capturam e não podem ser nomeadas This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' A tarefa esperada retorna '{0}' @@ -205,6 +215,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess Alterar '{0}' para '{1}' impedirá a continuação da sessão de depuração porque altera a forma da máquina de estado. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Configurar estilo de código de {0} @@ -2336,6 +2351,11 @@ Se o especificador de formato "H" for usado sem outros especificadores de format O especificador de formato personalizado "HH" (mais qualquer número de especificadores "H" adicionais) representa a hora como um número de 00 a 23. Ou seja, a hora é representada por um relógio de 24 horas com base em zero que conta as horas desde a meia-noite. Uma hora de dígito único é formatada com um zero à esquerda. + + code + code + + date separator separador de data @@ -2492,6 +2512,11 @@ O especificador de formato padrão "f" representa uma combinação de padrões d O especificador de formato padrão "T" representa uma cadeia de caracteres de formato personalizado de data e hora que é definida pela propriedade DateTimeFormatInfo.LongTimePattern de uma cultura específica. Por exemplo, a cadeia de caracteres de formato personalizado para a cultura invariável é "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) minuto (1-2 dígitos) @@ -2944,11 +2969,6 @@ Se o especificador de formato "g" for usado sem outros especificadores de format Atualizar uma instrução complexa contendo uma expressão aguardar impedirá que a sessão de depuração continue. - - Changing visibility of a constructor will prevent the debug session from continuing. - Alteração da visibilidade de um construtor impedirá a sessão de depuração de continuar. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. A captura da variável '{0}' que não havia sido capturada antes impedirá a sessão de depuração de continuar. @@ -3155,8 +3175,8 @@ Se o especificador de formato "g" for usado sem outros especificadores de format - Deleting '{0}' will prevent the debug session from continuing. - Excluir "{0}" impedirá que a sessão de depuração continue. + Deleting {0} will prevent the debug session from continuing. + Excluir "{0}" impedirá que a sessão de depuração continue. @@ -3174,11 +3194,6 @@ Se o especificador de formato "g" for usado sem outros especificadores de format Excluir um corpo de método impedirá que a sessão de depuração continue. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Uma instrução ativa foi removida de seu método original. Você deve reverter as alterações para continuar ou reiniciar a sessão de depuração. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. A atualização do modificador "async" ou "iterator" em torno de uma instrução ativa impedirá a sessão de depuração de continuar. @@ -3209,11 +3224,6 @@ Se o especificador de formato "g" for usado sem outros especificadores de format Modificar o inicializador de "{0}" em um tipo genérico impedirá que a sessão de depuração continue. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Modificar o inicializador de "{0}" em um tipo parcial impedirá que a sessão de depuração continue. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Adicionar um construtor a um tipo com um campo ou inicializador que propriedade com função anônima impedirá que a sessão de depuração continue. @@ -3265,8 +3275,8 @@ Se o especificador de formato "g" for usado sem outros especificadores de format - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Remover "{0}" que contém uma instrução ativa impedirá que a sessão de depuração continue. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Remover "{0}" que contém uma instrução ativa impedirá que a sessão de depuração continue. @@ -3660,6 +3670,11 @@ O objetivo do especificador de formato "s" é produzir cadeias de caracteres de Quando esse especificador de formato padrão é usado, a operação de análise ou formatação sempre usa a cultura invariável. + + static constructor + static constructor + + 'symbol' cannot be a namespace. "símbolo" não pode ser um namespace. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index e43964cfd9924..d88360bb1f41f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -90,6 +90,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Добавить имя элемента кортежа "{0}" + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Добавление "{0}" в метод интерфейса сделает продолжение сеанса отладки невозможным. @@ -125,6 +130,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Условия чередования не выполняют запись, и им невозможно присвоить имя This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' Ожидаемая задача возвращает "{0}". @@ -205,6 +215,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Изменение "{0}" на "{1}" сделает продолжение сеанса отладки невозможным, так как меняет форму конечной машины. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style Настройка стиля кода {0} @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's Описатель пользовательского формата "HH" (плюс любое число дополнительных описателей "H") представляет час в виде числа от 00 до 23, то есть час представлен в начинающемся с нуля 24-часовом формате, отсчитывающем часы с полуночи. Значение часа из одной цифры форматируется с начальным нулем. + + code + code + + date separator разделитель компонентов даты @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" Описатель стандартного формата "T" представляет строку пользовательского формата даты и времени, определяемую свойством DateTimeFormatInfo.LongTimePattern конкретных языка и региональных параметров. Например, строка пользовательского формата для инвариантных языка и региональных параметров имеет вид "HH:mm:ss". + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) минута (1–2 цифры) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Обновление комплексной инструкции с выражением Await помешает продолжению сеанса отладки. - - Changing visibility of a constructor will prevent the debug session from continuing. - Изменение видимости конструктора помешает продолжению сеанса отладки. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Запись переменной "{0}", которая не была записана прежде, помешает продолжению сеанса отладки. @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - Удаление "{0}" сделает продолжение сеанса отладки невозможным. + Deleting {0} will prevent the debug session from continuing. + Удаление "{0}" сделает продолжение сеанса отладки невозможным. @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Удаление тела метода сделает продолжение сеанса отладки невозможным. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Активный оператор был удален из исходного метода. Чтобы продолжить выполнение сеанса отладки, необходимо отменить изменения или перезапустить сеанс. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Обновление модификатора async или iterator рядом с активным оператором сделает продолжение сеанса отладки невозможным. @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Изменение инициализатора объекта "{0}" в универсальном типе сделает продолжение сеанса отладки невозможным. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Изменение инициализатора объекта "{0}" в разделяемом типе сделает продолжение сеанса отладки невозможным. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. При добавлении конструктора в тип с инициализатором поля или свойства, содержащим анонимную функцию, будет невозможно продолжить сеанс отладки. @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Удаление "{0}", содержащего активный оператор, сделает продолжение сеанса отладки невозможным. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Удаление "{0}", содержащего активный оператор, сделает продолжение сеанса отладки невозможным. @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation Когда используется этот описатель стандартного формата, операция форматирования или анализа всегда использует инвариантные язык и региональные параметры. + + static constructor + static constructor + + 'symbol' cannot be a namespace. '"символ" не может быть пространством имен. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 9bcba1c7c478a..c7b1accb0864d 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -90,6 +90,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be '{0}' demet öğesi adını ekle + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. Arabirim yöntemine '{0}' eklenmesi, hata ayıklama oturumunun devam etmesini engeller. @@ -125,6 +130,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be Değişim koşulları yakalamak değil ve adlandırılamaz This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' Beklenen görev '{0}' döndürüyor @@ -205,6 +215,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be '{0}' öğesini '{1}' olarak değiştirmek, durum makinesinin şeklini değiştirdiğinden hata ayıklama oturumunun devam etmesini engeller. + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style {0} kod stilini yapılandır @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's "HH" özel biçim belirticisi (ve herhangi bir sayıda ek "H" belirticisi) saati 00 ile 23 arasında bir sayı ile temsil eder; yani saat, gece yarısından bu yana geçen saat sayısını belirten sıfır tabanlı 24 saatlik bir düzen ile temsil edilir. Tek haneli bir saat, başına sıfır eklenerek biçimlendirilir. + + code + code + + date separator tarih ayırıcısı @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" "T" standart biçim belirticisi, belirli bir kültürün DateTimeFormatInfo.LongTimePattern özelliği tarafından tanımlanan bir özel tarih ve saat biçimi dizesini temsil eder. Örneğin, sabit kültür için özel biçim dizesi "HH:mm:ss" şeklindedir. + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) dakika (1-2 rakam) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Await ifadesi içeren bir karmaşık deyimin güncelleştirilmesi, hata ayıklama işleminin devam etmesini engeller. - - Changing visibility of a constructor will prevent the debug session from continuing. - Bir oluşturucunun görünürlüğünü değiştirmek, hata ayıklama oturumunun devam etmesini engeller. - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. Daha önce yakalanmayan '{0}' değişkeninin yakalanması hata ayıklama oturumunun devam etmesini engeller. @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - '{0}' öğesini silme, hata ayıklama oturumunun devam etmesini engelleyecek. + Deleting {0} will prevent the debug session from continuing. + '{0}' öğesini silme, hata ayıklama oturumunun devam etmesini engelleyecek. @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Bir yöntem gövdesi silme, hata ayıklama oturumunun devam etmesini engelleyecek. - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - Etkin bir deyim özgün yönteminden kaldırılmış. Devam etmek için değişikliklerinizi geri almalısınız veya hata ayıklama oturumunu yeniden başlatmalısınız. - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. Etkin bir deyimin etrafındaki async ya da iterator değiştiricisini güncelleştirmek hata ayıklama oturumunun devam etmesini önler. @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's Genel bir tür içindeki '{0}' öğesinin başlatıcısını değiştirme, hata ayıklama oturumunun devam etmesini engelleyecek. - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - Kısmi bir tür içindeki '{0}' öğesinin başlatıcısını değiştirme, hata ayıklama oturumunun devam etmesini engelleyecek. - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. Anonim bir tür içeren alan veya özellik başlatıcısının bulunduğu bir türe oluşturucu eklenmesi, hata ayıklama oturumunun devam etmesini engelleyebilir. @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - Etkin bir deyim içeren '{0}' öğesini kaldırma, hata ayıklama oturumunun devam etmesini engelleyecek. + Removing {0} that contains an active statement will prevent the debug session from continuing. + Etkin bir deyim içeren '{0}' öğesini kaldırma, hata ayıklama oturumunun devam etmesini engelleyecek. @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation Bu standart biçim belirticisi kullanıldığında, biçimlendirme veya ayrıştırma işlemi her zaman sabit kültürü kullanır. + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'symbol' bir ad alanı olamaz. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 8449cb520df6d..f2f4f83ed175e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -90,6 +90,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 添加元组元素名称 "{0}" + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. 将 "{0}" 添加进接口方法将阻止调试会话继续。 @@ -125,6 +130,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替换条件不捕获且不能命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' 等待任务返回“{0}” @@ -205,6 +215,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 将“{0}”更改为“{1}”会阻止调试会话继续执行,因为它会更改状态机的形状。 + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style 配置 {0} 代码样式 @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's "HH" 自定义格式说明符(另加任意数量的 "H" 说明符)将小时表示为从 00 至 23 的数字,即通过从零开始的 24 小时制表示小时,自午夜开始对小时计数。一位数字的小时数设置为带前导零的格式。 + + code + code + + date separator 日期分隔符 @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" "T" 标准格式说明符表示由特定区域性的 DateTimeFormatInfo.LongTimePattern 属性定义的自定义日期和时间格式字符串。例如,固定区域性的自定义格式字符串为 "HH:mm:ss"。 + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) 分钟(1-2 位数) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新包含 Await 表达式的复杂语句将中止调试会话。 - - Changing visibility of a constructor will prevent the debug session from continuing. - 更改构造函数的可见性将会中止调试会话。 - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. 捕获之前尚未捕获过的变量“{0}”将阻止调试会话继续执行。 @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - 删除“{0}”将阻止调试会话继续。 + Deleting {0} will prevent the debug session from continuing. + 删除“{0}”将阻止调试会话继续。 @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 删除方法主体将阻止调试会话继续。 - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - 活动语句已从其初始方法中删除。必须还原更改才能继续或重新启动调试会话。 - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. 更新当前语句的 async 或 iterator 修饰符可以阻止调试会话继续。 @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 修改泛型类型中的“{0}”的初始值设定项将阻止调试会话继续。 - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - 修改分部类型中“{0}”的初始值设定项将阻止调试会话继续。 - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. 使用包含匿名类型的字段或属性初始值设定项向类型添加构造函数将阻止调试会话继续。 @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - 删除含活动语句的“{0}”将阻止调试会话继续。 + Removing {0} that contains an active statement will prevent the debug session from continuing. + 删除含活动语句的“{0}”将阻止调试会话继续。 @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation 使用此标准格式说明符时,格式设置或分析操作始终使用固定区域性。 + + static constructor + static constructor + + 'symbol' cannot be a namespace. '“symbol” 不能为命名空间。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index eda7ed185b7c9..5e5bf32fe4c56 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -90,6 +90,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 新增元組元素名稱 ‘{0}’ + + Adding '{0}' into a generic type will prevent the debug session from continuing. + Adding '{0}' into a generic type will prevent the debug session from continuing. + + Adding '{0}' into an interface method will prevent the debug session from continuing. 將 '{0}' 新增至介面方法,會造成偵錯工作階段無法繼續。 @@ -125,6 +130,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 替代條件不會擷取,也無法命名 This is an error message shown to the user when they write an invalid Regular Expression. Example: (?(?'x')) + + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. + + Awaited task returns '{0}' 等待的工作會傳回 '{0}' @@ -205,6 +215,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 將 '{0}' 變更為 '{1}' 會變更狀態機器的圖形,進而使偵錯工作階段無法繼續。 + + Changing visibility of {0} will prevent the debug session from continuing. + Changing visibility of {0} will prevent the debug session from continuing. + + Configure {0} code style 設定 {0} 程式碼樣式 @@ -2336,6 +2351,11 @@ If the "H" format specifier is used without other custom format specifiers, it's "HH" 自訂格式規範 (加上任意數目的其他 "H" 規範) 代表小時,以數字 00 到 23 表示; 換句話說,小時即為自午夜起所經過的時數,是以零開始的 24 小時制。單一數字的小時格式,開頭不會出現零。 + + code + code + + date separator 日期分隔符號 @@ -2492,6 +2512,11 @@ The "f" standard format specifier represents a combination of the long date ("D" "T" 標準格式規範代表由特定文化特性的 DateTimeFormatInfo.LongTimePattern 屬性所定義的自訂日期與時間格式字串。例如,不因文化特性而異的自訂格式字串為 "HH:mm:ss"。 + + {0} '{1}' + {0} '{1}' + e.g. "method 'M'" + minute (1-2 digits) 分鐘 (1-2 位數) @@ -2944,11 +2969,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 更新包含 await 運算式的複雜陳述式會阻礙偵錯工作階段繼續進行。 - - Changing visibility of a constructor will prevent the debug session from continuing. - 變更建構函式的可見度會阻礙偵錯工作階段繼續進行。 - - Capturing variable '{0}' that hasn't been captured before will prevent the debug session from continuing. 擷取先前從未擷取過的變數 '{0}',會讓偵錯工作階段無法繼續。 @@ -3155,8 +3175,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Deleting '{0}' will prevent the debug session from continuing. - 刪除 '{0}' 會造成偵錯工作階段無法繼續。 + Deleting {0} will prevent the debug session from continuing. + 刪除 '{0}' 會造成偵錯工作階段無法繼續。 @@ -3174,11 +3194,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 刪除方法主體,會造成偵錯工作階段無法繼續。 - - An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session. - 現用陳述式已從其原始方法中移除。您必須還原您的變更才能繼續,或是重新啟動偵錯工作階段。 - - Updating async or iterator modifier around an active statement will prevent the debug session from continuing. 更新作用中陳述式前後的 async 或 iterator 修飾元,會造成偵錯工作階段無法繼續。 @@ -3209,11 +3224,6 @@ If the "g" format specifier is used without other custom format specifiers, it's 修改泛型類別中 '{0}' 的初始設定式,會造成偵錯工作階段無法繼續。 - - Modifying the initializer of '{0}' in a partial type will prevent the debug session from continuing. - 修改部分類型之 '{0}' 的初始設定式,會造成偵錯工作階段無法繼續。 - - Adding a constructor to a type with a field or property initializer that contains an anonymous function will prevent the debug session from continuing. 將建構函式加入類型,並附上包含會讓偵錯工作階段無法繼續的匿名函式欄位或屬性初始設定式。 @@ -3265,8 +3275,8 @@ If the "g" format specifier is used without other custom format specifiers, it's - Removing '{0}' that contains an active statement will prevent the debug session from continuing. - 移除包含現用陳述式的 '{0}',會造成偵錯工作階段無法繼續。 + Removing {0} that contains an active statement will prevent the debug session from continuing. + 移除包含現用陳述式的 '{0}',會造成偵錯工作階段無法繼續。 @@ -3660,6 +3670,11 @@ When this standard format specifier is used, the formatting or parsing operation 使用此標準格式規範時,格式化或剖析作業永遠不因文化特性而異。 + + static constructor + static constructor + + 'symbol' cannot be a namespace. 'symbol' 不可為命名空間。 diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/TopSyntaxComparer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/TopSyntaxComparer.vb index c6fb5325d9d3e..dde1f58dc3e0c 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/TopSyntaxComparer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/TopSyntaxComparer.vb @@ -11,38 +11,95 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Friend Shared ReadOnly Instance As TopSyntaxComparer = New TopSyntaxComparer() + Private ReadOnly _oldRoot As SyntaxNode + Private ReadOnly _newRoot As SyntaxNode + Private ReadOnly _oldRootChildren As IEnumerable(Of SyntaxNode) + Private ReadOnly _newRootChildren As IEnumerable(Of SyntaxNode) + Private Sub New() End Sub + Public Sub New(oldRoot As SyntaxNode, newRoot As SyntaxNode, oldRootChildren As IEnumerable(Of SyntaxNode), newRootChildren As IEnumerable(Of SyntaxNode)) + ' explicitly listed roots And all their children must be labeled: + Debug.Assert(HasLabel(oldRoot)) + Debug.Assert(HasLabel(newRoot)) + Debug.Assert(oldRootChildren.All(Function(n) HasLabel(n))) + Debug.Assert(newRootChildren.All(Function(n) HasLabel(n))) + + _oldRoot = oldRoot + _newRoot = newRoot + _oldRootChildren = oldRootChildren + _newRootChildren = newRootChildren + + ' the virtual parent of root children must be the respective root + Dim parent As SyntaxNode = Nothing + Debug.Assert(Not TryGetParent(oldRoot, parent)) + Debug.Assert(Not TryGetParent(newRoot, parent)) + Debug.Assert(oldRootChildren.All(Function(node) TryGetParent(node, parent) AndAlso parent Is oldRoot)) + Debug.Assert(newRootChildren.All(Function(node) TryGetParent(node, parent) AndAlso parent Is newRoot)) + End Sub + #Region "Tree Traversal" Protected Overrides Function TryGetParent(node As SyntaxNode, ByRef parent As SyntaxNode) As Boolean - Dim parentNode = node.Parent - parent = parentNode - Return parentNode IsNot Nothing + If node Is _oldRoot OrElse node Is _newRoot Then + parent = Nothing + Return False + End If + + parent = node.Parent + While parent IsNot Nothing AndAlso Not HasLabel(parent) + parent = parent.Parent + End While + + Return parent IsNot Nothing End Function Protected Overrides Function GetChildren(node As SyntaxNode) As IEnumerable(Of SyntaxNode) - Debug.Assert(GetLabel(node) <> IgnoredNode) + Debug.Assert(HasLabel(node)) + + If node Is _oldRoot Then + Return _oldRootChildren + End If + + If node Is _newRoot Then + Return _newRootChildren + End If + Return If(HasChildren(node), EnumerateChildren(node), Nothing) End Function - Private Iterator Function EnumerateChildren(node As SyntaxNode) As IEnumerable(Of SyntaxNode) + Private Shared Iterator Function EnumerateChildren(node As SyntaxNode) As IEnumerable(Of SyntaxNode) For Each child In node.ChildNodesAndTokens() Dim childNode = child.AsNode() - If childNode IsNot Nothing AndAlso GetLabel(childNode) <> IgnoredNode Then + If childNode IsNot Nothing AndAlso HasLabel(childNode) Then Yield childNode End If Next End Function - Protected Overrides Iterator Function GetDescendants(node As SyntaxNode) As IEnumerable(Of SyntaxNode) + Protected Overrides Function GetDescendants(node As SyntaxNode) As IEnumerable(Of SyntaxNode) + Dim rootChildren = If(node Is _oldRoot, _oldRootChildren, If(node Is _newRoot, _newRootChildren, Nothing)) + Return If(rootChildren IsNot Nothing, EnumerateDescendants(rootChildren), EnumerateDescendants(node)) + End Function + + Private Shared Iterator Function EnumerateDescendants(nodes As IEnumerable(Of SyntaxNode)) As IEnumerable(Of SyntaxNode) + For Each node In nodes + Yield node + + For Each descendant In EnumerateDescendants(node) + Yield descendant + Next + Next + End Function + + Private Shared Iterator Function EnumerateDescendants(node As SyntaxNode) As IEnumerable(Of SyntaxNode) For Each descendant In node.DescendantNodesAndTokens( descendIntoChildren:=AddressOf HasChildren, descendIntoTrivia:=False) Dim descendantNode = descendant.AsNode() - If descendantNode IsNot Nothing AndAlso GetLabel(descendantNode) <> IgnoredNode Then + If descendantNode IsNot Nothing AndAlso HasLabel(descendantNode) Then Yield descendantNode End If Next @@ -107,7 +164,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ParameterList ' tied to parent Parameter ' tied to parent - FieldOrParameterName ' tied to grandparent (FieldDeclaration or ParameterList) + FieldOrParameterName ' tied to grand-grandparent (type or method declaration) SimpleAsClause ' tied to parent AttributeList ' tied to parent @@ -152,7 +209,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return 1 Case Label.FieldOrParameterName - Return 2 ' FieldDeclaration or ParameterList + Return 3 ' type or method declaration Case Else Return 0 @@ -334,6 +391,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Classify(kind, isLeaf, ignoreVariableDeclarations) <> Label.Ignored End Function + Friend Shared Function HasLabel(node As SyntaxNode) As Boolean + Return HasLabel(node.Kind, ignoreVariableDeclarations:=False) + End Function + Protected Overrides ReadOnly Property LabelCount As Integer Get Return Label.Count diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index eecc1443df2a2..21e382aa2f5dc 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -42,8 +42,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ''' ''' for methods, constructors, operators and accessors. ''' for auto-properties. - ''' for fields with simple initialization "Dim a = 1", or "Dim a As New C" - ''' for fields with shared AsNew initialization "Dim a, b As New C" or array initializer "Dim a(n), b(n)". + ''' for fields with single identifier in the declaration. + ''' for fields with multiple identifiers in the declaration. ''' A null reference otherwise. ''' Friend Overrides Function FindMemberDeclaration(rootOpt As SyntaxNode, node As SyntaxNode) As SyntaxNode @@ -61,31 +61,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return node Case SyntaxKind.PropertyStatement - ' Property [|a As Integer = 1|] - ' Property [|a As New C()|] + ' Property a As Integer = 1 + ' Property a As New T If Not node.Parent.IsKind(SyntaxKind.PropertyBlock) Then Return node End If Case SyntaxKind.VariableDeclarator - If node.Parent.IsKind(SyntaxKind.FieldDeclaration) Then - ' Dim [|a = 0|] - ' Dim [|a = 0|], [|b = 0|] - ' Dim [|b as Integer = 0|] - ' Dim [|v1 As New C|] - ' Dim v1, v2 As New C(Sub [|Goo()|]) + ' Dim a = 0 + ' Dim a(n) = 0 + ' Dim a = 0, b = 0 + ' Dim b as Integer = 0 + ' Dim b(n) as Integer = 0 + ' Dim a As New T + If IsFieldDeclaration(CType(node, VariableDeclaratorSyntax)) Then Return node End If Case SyntaxKind.ModifiedIdentifier - ' Dim [|a(n)|], [|b(n)|] As Integer - ' Dim [|v1|], [|v2|] As New C - If Not node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then - Exit Select - End If - - If DirectCast(node, ModifiedIdentifierSyntax).ArrayBounds IsNot Nothing OrElse - DirectCast(node.Parent, VariableDeclaratorSyntax).Names.Count > 1 Then + ' Dim a, b As T + ' Dim a(n), b(n) As T + ' Dim a, b As New T + If IsFieldDeclaration(CType(node, ModifiedIdentifierSyntax)) Then Return node End If End Select @@ -96,14 +93,28 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing End Function + ''' + ''' Returns true if the node represents a field declaration. + ''' + Private Shared Function IsFieldDeclaration(node As ModifiedIdentifierSyntax) As Boolean + Return node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) AndAlso DirectCast(node.Parent, VariableDeclaratorSyntax).Names.Count > 1 + End Function + + ''' + ''' Returns true if the node represents a field declaration. + ''' + Private Shared Function IsFieldDeclaration(node As VariableDeclaratorSyntax) As Boolean + Return node.Parent.IsKind(SyntaxKind.FieldDeclaration) AndAlso node.Names.Count = 1 + End Function + ''' - ''' Given a node representing a declaration ( = true) or a top-level edit node ( = false) returns: + ''' Given a node representing a declaration or a top-level edit node returns: ''' - for methods, constructors, operators and accessors. ''' - for auto-properties and fields with initializer or AsNew clause. ''' - for fields with array initializer, e.g. "Dim a(1) As Integer". ''' A null reference otherwise. ''' - Friend Overrides Function TryGetDeclarationBody(node As SyntaxNode, isMember As Boolean) As SyntaxNode + Friend Overrides Function TryGetDeclarationBody(node As SyntaxNode) As SyntaxNode Select Case node.Kind Case SyntaxKind.SubBlock, SyntaxKind.FunctionBlock, @@ -136,43 +147,55 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing End If - ' Dim a = initializer Dim variableDeclarator = DirectCast(node, VariableDeclaratorSyntax) + + Dim body As SyntaxNode = Nothing + If variableDeclarator.Initializer IsNot Nothing Then - Return variableDeclarator.Initializer.Value + ' Dim a = initializer + body = variableDeclarator.Initializer.Value + ElseIf HasAsNewClause(variableDeclarator) Then + ' Dim a As New T + ' Dim a,b As New T + body = DirectCast(variableDeclarator.AsClause, AsNewClauseSyntax).NewExpression End If - If HasAsNewClause(variableDeclarator) Then - ' Dim a As New C() - ' Dim a, b As New C(), but only if the specified node isn't already a member declaration representative. - ' -- This is to handle an edit in AsNew clause because such an edit doesn't affect the modified identifier that would otherwise represent the member. - If variableDeclarator.Names.Count = 1 OrElse Not isMember Then - Return DirectCast(variableDeclarator.AsClause, AsNewClauseSyntax).NewExpression + ' Dim a(n) As T + If variableDeclarator.Names.Count = 1 Then + Dim name = variableDeclarator.Names(0) + + If name.ArrayBounds IsNot Nothing Then + ' Initializer and AsNew clause can't be syntactically specified at the same time, but array bounds can be (it's a semantic error). + ' Guard against such case to maintain consistency and set body to Nothing in that case. + body = If(body Is Nothing, name.ArrayBounds, Nothing) End If End If - Return Nothing + Return body Case SyntaxKind.ModifiedIdentifier If Not node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then Return Nothing End If + Dim modifiedIdentifier = CType(node, ModifiedIdentifierSyntax) + Dim body As SyntaxNode = Nothing + ' Dim a, b As New C() Dim variableDeclarator = DirectCast(node.Parent, VariableDeclaratorSyntax) - If HasMultiAsNewInitializer(variableDeclarator) Then - Return DirectCast(variableDeclarator.AsClause, AsNewClauseSyntax).NewExpression + If HasAsNewClause(variableDeclarator) Then + body = DirectCast(variableDeclarator.AsClause, AsNewClauseSyntax).NewExpression End If - ' Dim a(n) + ' Dim a(n) As Integer ' Dim a(n), b(n) As Integer - ' Dim a(n1, n2, n3) As Integer - Dim modifiedIdentifier = DirectCast(node, ModifiedIdentifierSyntax) If modifiedIdentifier.ArrayBounds IsNot Nothing Then - Return modifiedIdentifier.ArrayBounds + ' AsNew clause can be syntactically specified at the same time as array bounds can be (it's a semantic error). + ' Guard against such case to maintain consistency and set body to Nothing in that case. + body = If(body Is Nothing, modifiedIdentifier.ArrayBounds, Nothing) End If - Return Nothing + Return body Case Else ' Note: A method without body is represented by a SubStatement. @@ -245,14 +268,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Select node End Function - Private Shared Function HasSimpleAsNewInitializer(variableDeclarator As VariableDeclaratorSyntax) As Boolean - Return variableDeclarator.Names.Count = 1 AndAlso HasAsNewClause(variableDeclarator) - End Function - - Private Shared Function HasMultiAsNewInitializer(variableDeclarator As VariableDeclaratorSyntax) As Boolean - Return variableDeclarator.Names.Count > 1 AndAlso HasAsNewClause(variableDeclarator) - End Function - Private Shared Function HasAsNewClause(variableDeclarator As VariableDeclaratorSyntax) As Boolean Return variableDeclarator.AsClause IsNot Nothing AndAlso variableDeclarator.AsClause.IsKind(SyntaxKind.AsNewClause) End Function @@ -300,7 +315,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing Case SyntaxKind.VariableDeclarator - If Not node.Parent.IsKind(SyntaxKind.FieldDeclaration) Then + Dim variableDeclarator = DirectCast(node, VariableDeclaratorSyntax) + If Not IsFieldDeclaration(variableDeclarator) Then Return Nothing End If @@ -311,32 +327,36 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If ' Dim a = initializer - Dim variableDeclarator = DirectCast(node, VariableDeclaratorSyntax) If variableDeclarator.Initializer IsNot Nothing Then Return variableDeclarator.DescendantTokens() End If ' Dim a As New C() - If HasSimpleAsNewInitializer(variableDeclarator) Then + If HasAsNewClause(variableDeclarator) Then + Return variableDeclarator.DescendantTokens() + End If + + ' Dim a(n) As Integer + Dim modifiedIdentifier = variableDeclarator.Names.Single() + If modifiedIdentifier.ArrayBounds IsNot Nothing Then Return variableDeclarator.DescendantTokens() End If Return Nothing Case SyntaxKind.ModifiedIdentifier - If Not node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then + Dim modifiedIdentifier = DirectCast(node, ModifiedIdentifierSyntax) + If Not IsFieldDeclaration(modifiedIdentifier) Then Return Nothing End If ' Dim a, b As New C() Dim variableDeclarator = DirectCast(node.Parent, VariableDeclaratorSyntax) - If HasMultiAsNewInitializer(variableDeclarator) Then + If HasAsNewClause(variableDeclarator) Then Return node.DescendantTokens().Concat(DirectCast(variableDeclarator.AsClause, AsNewClauseSyntax).NewExpression.DescendantTokens()) End If - ' Dim a(n) ' Dim a(n), b(n) As Integer - Dim modifiedIdentifier = DirectCast(node, ModifiedIdentifierSyntax) If modifiedIdentifier.ArrayBounds IsNot Nothing Then Return node.DescendantTokens() End If @@ -522,7 +542,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Debug.Assert(leftSymbol IsNot Nothing) Dim rightProperty = rightType.GetMembers(leftSymbol.Name).Single() - Dim rightDeclaration = DirectCast(GetSymbolSyntax(rightProperty, cancellationToken), PropertyStatementSyntax) + Dim rightDeclaration = DirectCast(rightProperty.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken), PropertyStatementSyntax) rightInitializer = rightDeclaration.Initializer ElseIf leftInitializer.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then @@ -533,7 +553,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Debug.Assert(leftSymbol IsNot Nothing) Dim rightSymbol = rightType.GetMembers(leftSymbol.Name).Single() - Dim rightDeclarator = DirectCast(GetSymbolSyntax(rightSymbol, cancellationToken).Parent, VariableDeclaratorSyntax) + Dim rightDeclarator = DirectCast(rightSymbol.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken).Parent, VariableDeclaratorSyntax) rightInitializer = If(leftInitializer.IsKind(SyntaxKind.EqualsValue), rightDeclarator.Initializer, DirectCast(rightDeclarator.AsClause, SyntaxNode)) Else @@ -548,7 +568,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Debug.Assert(leftSymbol IsNot Nothing) Dim rightSymbol = rightType.GetMembers(leftSymbol.Name).Single() - Dim rightIdentifier = DirectCast(GetSymbolSyntax(rightSymbol, cancellationToken), ModifiedIdentifierSyntax) + Dim rightIdentifier = DirectCast(rightSymbol.DeclaringSyntaxReferences.Single().GetSyntax(cancellationToken), ModifiedIdentifierSyntax) rightInitializer = rightIdentifier.ArrayBounds.Arguments(argumentIndex) End If @@ -603,6 +623,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return TopSyntaxComparer.Instance.ComputeMatch(oldCompilationUnit, newCompilationUnit) End Function + Protected Overrides Function ComputeTopLevelDeclarationMatch(oldDeclaration As SyntaxNode, newDeclaration As SyntaxNode) As Match(Of SyntaxNode) + Contract.ThrowIfNull(oldDeclaration.Parent) + Contract.ThrowIfNull(newDeclaration.Parent) + + ' Allow matching field declarations represented by a identitifer and the whole variable declarator + ' even when their node kinds do not match. + If oldDeclaration.IsKind(SyntaxKind.ModifiedIdentifier) AndAlso newDeclaration.IsKind(SyntaxKind.VariableDeclarator) Then + oldDeclaration = oldDeclaration.Parent + ElseIf oldDeclaration.IsKind(SyntaxKind.VariableDeclarator) AndAlso newDeclaration.IsKind(SyntaxKind.ModifiedIdentifier) Then + newDeclaration = newDeclaration.Parent + End If + + Dim comparer = New TopSyntaxComparer(oldDeclaration.Parent, newDeclaration.Parent, {oldDeclaration}, {newDeclaration}) + Return comparer.ComputeMatch(oldDeclaration.Parent, newDeclaration.Parent) + End Function + Protected Overrides Function ComputeBodyMatch(oldBody As SyntaxNode, newBody As SyntaxNode, knownMatches As IEnumerable(Of KeyValuePair(Of SyntaxNode, SyntaxNode))) As Match(Of SyntaxNode) SyntaxUtilities.AssertIsBody(oldBody, allowLambda:=True) SyntaxUtilities.AssertIsBody(newBody, allowLambda:=True) @@ -911,7 +947,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Function Friend Overrides Function TryGetContainingTypeDeclaration(node As SyntaxNode) As SyntaxNode - Return node.Parent.FirstAncestorOrSelf(Of TypeBlockSyntax)() + Return node.Parent.FirstAncestorOrSelf(Of TypeBlockSyntax)() ' TODO: EnbumBlock? + End Function + + Friend Overrides Function TryGetAssociatedMemberDeclaration(node As SyntaxNode) As SyntaxNode + Return If(node.IsParentKind(SyntaxKind.PropertyBlock, SyntaxKind.EventBlock), node.Parent, Nothing) End Function Friend Overrides Function HasBackingField(propertyDeclaration As SyntaxNode) As Boolean @@ -959,6 +999,95 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing End Function + ''' + ''' VB symbols return references that represent the declaration statement. + ''' The node that represenets the whole declaration (the block) is the parent node if it exists. + ''' For example, a method with a body is represented by a SubBlock/FunctionBlock while a method without a body + ''' is represented by its declaration statement. + ''' + Protected Overrides Function GetSymbolDeclarationSyntax(reference As SyntaxReference, cancellationToken As CancellationToken) As SyntaxNode + Dim syntax = reference.GetSyntax(cancellationToken) + Dim parent = syntax.Parent + + Select Case syntax.Kind + ' declarations that always have block + + Case SyntaxKind.NamespaceStatement + Debug.Assert(parent.Kind = SyntaxKind.NamespaceBlock) + Return parent + + Case SyntaxKind.ClassStatement + Debug.Assert(parent.Kind = SyntaxKind.ClassBlock) + Return parent + + Case SyntaxKind.StructureStatement + Debug.Assert(parent.Kind = SyntaxKind.StructureBlock) + Return parent + + Case SyntaxKind.InterfaceStatement + Debug.Assert(parent.Kind = SyntaxKind.InterfaceBlock) + Return parent + + Case SyntaxKind.ModuleStatement + Debug.Assert(parent.Kind = SyntaxKind.ModuleBlock) + Return parent + + Case SyntaxKind.EnumStatement + Debug.Assert(parent.Kind = SyntaxKind.EnumBlock) + Return parent + + Case SyntaxKind.SubNewStatement + Debug.Assert(parent.Kind = SyntaxKind.ConstructorBlock) + Return parent + + Case SyntaxKind.OperatorStatement + Debug.Assert(parent.Kind = SyntaxKind.OperatorBlock) + Return parent + + Case SyntaxKind.GetAccessorStatement + Debug.Assert(parent.Kind = SyntaxKind.GetAccessorBlock) + Return parent + + Case SyntaxKind.SetAccessorStatement + Debug.Assert(parent.Kind = SyntaxKind.SetAccessorBlock) + Return parent + + Case SyntaxKind.AddHandlerAccessorStatement + Debug.Assert(parent.Kind = SyntaxKind.AddHandlerAccessorBlock) + Return parent + + Case SyntaxKind.RemoveHandlerAccessorStatement + Debug.Assert(parent.Kind = SyntaxKind.RemoveHandlerAccessorBlock) + Return parent + + Case SyntaxKind.RaiseEventAccessorStatement + Debug.Assert(parent.Kind = SyntaxKind.RaiseEventAccessorBlock) + Return parent + + ' declarations that may or may not have block + + Case SyntaxKind.SubStatement + Return If(parent.Kind = SyntaxKind.SubBlock, parent, syntax) + + Case SyntaxKind.FunctionStatement + Return If(parent.Kind = SyntaxKind.FunctionBlock, parent, syntax) + + ' declarations that never have a block + + Case SyntaxKind.ModifiedIdentifier + Contract.ThrowIfFalse(parent.Parent.IsKind(SyntaxKind.FieldDeclaration)) + Dim variableDeclaration = CType(parent, VariableDeclaratorSyntax) + Return If(variableDeclaration.Names.Count = 1, parent, syntax) + + Case SyntaxKind.VariableDeclarator + ' fields are represented by ModifiedIdentifier: + Throw ExceptionUtilities.UnexpectedValue(syntax.Kind) + + Case Else + Return syntax + End Select + End Function + Friend Overrides Function IsConstructorWithMemberInitializers(declaration As SyntaxNode) As Boolean Dim ctor = TryCast(declaration, ConstructorBlockSyntax) If ctor Is Nothing Then @@ -1007,7 +1136,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue DirectCast(syntaxRefs.Single().GetSyntax(), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword) End Function - Protected Overrides Function GetSymbolForEdit(model As SemanticModel, node As SyntaxNode, editKind As EditKind, editMap As IReadOnlyDictionary(Of SyntaxNode, EditKind), cancellationToken As CancellationToken) As ISymbol + Protected Overrides Function GetSymbolForEdit(model As SemanticModel, + node As SyntaxNode, + editKind As EditKind, + editMap As IReadOnlyDictionary(Of SyntaxNode, EditKind), + ByRef isAmbiguous As Boolean, + cancellationToken As CancellationToken) As ISymbol + + isAmbiguous = False + ' Avoid duplicate semantic edits - don't return symbols for statements within blocks. Select Case node.Kind() Case SyntaxKind.OperatorStatement, @@ -1017,8 +1154,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.AddHandlerAccessorStatement, SyntaxKind.RemoveHandlerAccessorStatement, SyntaxKind.RaiseEventAccessorStatement, - SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement, SyntaxKind.ClassStatement, SyntaxKind.StructureStatement, SyntaxKind.InterfaceStatement, @@ -1047,27 +1182,37 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing End If - Case SyntaxKind.Parameter + Case SyntaxKind.Parameter, + SyntaxKind.TypeParameter, + SyntaxKind.ImportsStatement, + SyntaxKind.NamespaceBlock Return Nothing Case SyntaxKind.ModifiedIdentifier - If node.Parent.IsKind(SyntaxKind.Parameter) Then + If Not node.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration) Then Return Nothing End If Case SyntaxKind.VariableDeclarator - ' An update to a field variable declarator might either be - ' 1) variable declarator update (an initializer is changes) - ' 2) modified identifier update (an array bound changes) - ' Handle the first one here. - If editKind = editKind.Update AndAlso node.Parent.IsKind(SyntaxKind.FieldDeclaration) Then - ' If multiple fields are defined by this declaration pick the first one. - ' We want to analyze the associated initializer just once. Any of the fields is good. - node = DirectCast(node, VariableDeclaratorSyntax).Names.First() + If Not node.Parent.IsKind(SyntaxKind.FieldDeclaration) Then + Return Nothing End If + + Dim variableDeclarator = CType(node, VariableDeclaratorSyntax) + isAmbiguous = variableDeclarator.Names.Count > 1 + node = variableDeclarator.Names.First End Select - Return model.GetDeclaredSymbol(node, cancellationToken) + Dim symbol = model.GetDeclaredSymbol(node, cancellationToken) + + ' Ignore partial method definition parts. + ' Partial method that does not have implementation part is not emitted to metadata. + ' Partial method without a definition part is a compilation error. + If symbol IsNot Nothing AndAlso symbol.Kind = SymbolKind.Method AndAlso CType(symbol, IMethodSymbol).IsPartialDefinition Then + Return Nothing + End If + + Return symbol End Function Friend Overrides Function ContainsLambda(declaration As SyntaxNode) As Boolean @@ -1226,7 +1371,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.FunctionBlock, SyntaxKind.OperatorBlock, SyntaxKind.ConstructorBlock, - SyntaxKind.EventBlock, SyntaxKind.SetAccessorBlock, SyntaxKind.GetAccessorBlock, SyntaxKind.AddHandlerAccessorBlock, @@ -1234,6 +1378,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.RaiseEventAccessorBlock Return GetDiagnosticSpan(DirectCast(node, MethodBlockBaseSyntax).BlockStatement) + Case SyntaxKind.EventBlock + Return GetDiagnosticSpan(DirectCast(node, EventBlockSyntax).EventStatement) + Case SyntaxKind.SubStatement, SyntaxKind.FunctionStatement, SyntaxKind.OperatorStatement, @@ -1598,9 +1745,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.OperatorStatement Return FeaturesResources.operator_ - Case SyntaxKind.ConstructorBlock, - SyntaxKind.SubNewStatement - Return FeaturesResources.constructor + Case SyntaxKind.ConstructorBlock + Return If(CType(node, ConstructorBlockSyntax).SubNewStatement.Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.shared_constructor, FeaturesResources.constructor) + + Case SyntaxKind.SubNewStatement + Return If(CType(node, SubNewStatementSyntax).Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.Shared_constructor, FeaturesResources.constructor) Case SyntaxKind.PropertyBlock @@ -1824,7 +1973,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Case EditKind.Move - ReportError(RudeEditKind.Move) + ClassifyMove(_newNode) Return Case EditKind.Insert @@ -1841,6 +1990,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Sub #Region "Move and Reorder" + Private Sub ClassifyMove(newNode As SyntaxNode) + Select Case newNode.Kind + Case SyntaxKind.ModifiedIdentifier, + SyntaxKind.VariableDeclarator + ' Identifier can be moved within the same type declaration. + ' Determine validity of such change in semantic analysis. + Return + + Case Else + ReportError(RudeEditKind.Move) + End Select + End Sub + Private Sub ClassifyReorder(oldNode As SyntaxNode, newNode As SyntaxNode) Select Case newNode.Kind Case SyntaxKind.OptionStatement, @@ -1885,8 +2047,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Case SyntaxKind.PropertyStatement, SyntaxKind.FieldDeclaration, - SyntaxKind.EventStatement, - SyntaxKind.VariableDeclarator + SyntaxKind.EventStatement ' Maybe we could allow changing order of field declarations unless the containing type layout is sequential, ' and it's not a COM interface. ReportError(RudeEditKind.Move) @@ -1903,11 +2064,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Move) Return - Case SyntaxKind.ModifiedIdentifier - ' Identifier can only moved from one VariableDeclarator to another if both are part of - ' the same declaration. We could allow these moves if the order and types of variables - ' didn't change. - ReportError(RudeEditKind.Move) + Case SyntaxKind.ModifiedIdentifier, + SyntaxKind.VariableDeclarator + ' Identifier can be moved within the same type declaration. + ' Determine validity of such change in semantic analysis. Return Case Else @@ -1927,93 +2087,37 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Case SyntaxKind.ClassBlock, - SyntaxKind.StructureBlock - ClassifyTypeWithPossibleExternMembersInsert(DirectCast(node, TypeBlockSyntax)) - Return - - Case SyntaxKind.InterfaceBlock - ClassifyTypeInsert(DirectCast(node, TypeBlockSyntax).BlockStatement.Modifiers) - Return - - Case SyntaxKind.EnumBlock - ClassifyTypeInsert(DirectCast(node, EnumBlockSyntax).EnumStatement.Modifiers) - Return - - Case SyntaxKind.ModuleBlock - ' Modules can't be nested or private - ReportError(RudeEditKind.Insert) - Return - - Case SyntaxKind.DelegateSubStatement, - SyntaxKind.DelegateFunctionStatement - ClassifyTypeInsert(DirectCast(node, DelegateStatementSyntax).Modifiers) - Return - - Case SyntaxKind.SubStatement, ' interface method - SyntaxKind.FunctionStatement ' interface method - ReportError(RudeEditKind.Insert) - Return - - Case SyntaxKind.PropertyBlock - ClassifyModifiedMemberInsert(DirectCast(node, PropertyBlockSyntax).PropertyStatement.Modifiers) - Return - - Case SyntaxKind.PropertyStatement ' autoprop or interface property - ' We don't need to check whether the container is an interface, since we disallow - ' adding public methods And all methods in interface declarations are public. - ClassifyModifiedMemberInsert(DirectCast(node, PropertyStatementSyntax).Modifiers) - Return - - Case SyntaxKind.EventBlock - ClassifyModifiedMemberInsert(DirectCast(node, EventBlockSyntax).EventStatement.Modifiers) - Return - - Case SyntaxKind.EventStatement - ClassifyModifiedMemberInsert(DirectCast(node, EventStatementSyntax).Modifiers) - Return - - Case SyntaxKind.OperatorBlock - ReportError(RudeEditKind.InsertOperator) - Return - - Case SyntaxKind.SubBlock, - SyntaxKind.FunctionBlock - ClassifyMethodInsert(DirectCast(node, MethodBlockSyntax).SubOrFunctionStatement) - Return - - Case SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement - ' CLR doesn't support adding P/Invokes - ReportError(RudeEditKind.Insert) - Return - - Case SyntaxKind.ConstructorBlock - If SyntaxUtilities.IsParameterlessConstructor(node) Then - Return - End If - - ClassifyModifiedMemberInsert(DirectCast(node, MethodBlockBaseSyntax).BlockStatement.Modifiers) - Return - - Case SyntaxKind.GetAccessorBlock, + SyntaxKind.StructureBlock, + SyntaxKind.InterfaceBlock, + SyntaxKind.EnumBlock, + SyntaxKind.ModuleBlock, + SyntaxKind.DelegateSubStatement, + SyntaxKind.DelegateFunctionStatement, + SyntaxKind.SubStatement, ' interface method + SyntaxKind.FunctionStatement, ' interface method + SyntaxKind.PropertyBlock, + SyntaxKind.PropertyStatement, ' autoprop or interface property + SyntaxKind.EventBlock, + SyntaxKind.EventStatement, + SyntaxKind.OperatorBlock, + SyntaxKind.SubBlock, + SyntaxKind.FunctionBlock, + SyntaxKind.DeclareSubStatement, + SyntaxKind.DeclareFunctionStatement, + SyntaxKind.ConstructorBlock, + SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock, SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, - SyntaxKind.RaiseEventAccessorBlock - Return - - Case SyntaxKind.FieldDeclaration - ClassifyFieldInsert(DirectCast(node, FieldDeclarationSyntax)) + SyntaxKind.RaiseEventAccessorBlock, + SyntaxKind.FieldDeclaration, + SyntaxKind.ModifiedIdentifier Return Case SyntaxKind.VariableDeclarator ' Ignore, errors will be reported for children (ModifiedIdentifier, AsClause) Return - Case SyntaxKind.ModifiedIdentifier - ClassifyFieldInsert(DirectCast(node, ModifiedIdentifierSyntax)) - Return - Case SyntaxKind.EnumMemberDeclaration, SyntaxKind.TypeParameter, SyntaxKind.StructureConstraint, @@ -2041,80 +2145,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Select End Sub - Private Function ClassifyModifiedMemberInsert(modifiers As SyntaxTokenList) As Boolean - If modifiers.Any(SyntaxKind.OverridableKeyword) OrElse - modifiers.Any(SyntaxKind.MustOverrideKeyword) OrElse - modifiers.Any(SyntaxKind.OverridesKeyword) Then - - ReportError(RudeEditKind.InsertVirtual) - Return False - End If - - Return True - End Function - - Private Function ClassifyTypeInsert(modifiers As SyntaxTokenList) As Boolean - Return ClassifyModifiedMemberInsert(modifiers) - End Function - - Private Sub ClassifyTypeWithPossibleExternMembersInsert(type As TypeBlockSyntax) - If Not ClassifyTypeInsert(type.BlockStatement.Modifiers) Then - Return - End If - - For Each member In type.Members - Dim modifiers As SyntaxTokenList - - Select Case member.Kind - Case SyntaxKind.DeclareFunctionStatement, - SyntaxKind.DeclareSubStatement - ReportError(RudeEditKind.Insert, member, member) - - Case SyntaxKind.PropertyStatement - modifiers = DirectCast(member, PropertyStatementSyntax).Modifiers - - Case SyntaxKind.SubBlock, - SyntaxKind.FunctionBlock - modifiers = DirectCast(member, MethodBlockBaseSyntax).BlockStatement.Modifiers - End Select - - ' TODO: DllImport/Declare? - 'If (modifiers.Any(SyntaxKind.MustOverrideKeyword)) Then - ' ReportError(RudeEditKind.InsertMustOverride, member, member) - 'End If - Next - End Sub - - Private Sub ClassifyMethodInsert(method As MethodStatementSyntax) - If method.TypeParameterList IsNot Nothing Then - ReportError(RudeEditKind.InsertGenericMethod) - End If - - If method.HandlesClause IsNot Nothing Then - ReportError(RudeEditKind.InsertHandlesClause) - End If - - ClassifyModifiedMemberInsert(method.Modifiers) - End Sub - - Private Sub ClassifyFieldInsert(field As FieldDeclarationSyntax) - ' Can't insert WithEvents field since it is effectively a virtual property. - If field.Modifiers.Any(SyntaxKind.WithEventsKeyword) Then - ReportError(RudeEditKind.Insert) - Return - End If - - Dim containingType = field.Parent - If containingType.IsKind(SyntaxKind.ModuleBlock) Then - ReportError(RudeEditKind.Insert) - Return - End If - End Sub - - Private Sub ClassifyFieldInsert(fieldVariableName As ModifiedIdentifierSyntax) - ClassifyFieldInsert(DirectCast(fieldVariableName.Parent.Parent, FieldDeclarationSyntax)) - End Sub - Private Sub ClassifyParameterInsert(parameterList As ParameterListSyntax) ' Sub M -> Sub M() is ok If parameterList.Parameters.Count = 0 Then @@ -2131,9 +2161,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Select Case oldNode.Kind Case SyntaxKind.OptionStatement, SyntaxKind.ImportsStatement, - SyntaxKind.AttributesStatement, SyntaxKind.NamespaceBlock, - SyntaxKind.ClassBlock, + SyntaxKind.AttributesStatement + ReportError(RudeEditKind.Delete) + Return + + Case SyntaxKind.ClassBlock, SyntaxKind.StructureBlock, SyntaxKind.InterfaceBlock, SyntaxKind.ModuleBlock, @@ -2152,30 +2185,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.EventBlock, SyntaxKind.EventStatement, SyntaxKind.DeclareSubStatement, - SyntaxKind.DeclareFunctionStatement - ' To allow removal of declarations we would need to update method bodies that - ' were previously binding to them but now are binding to another symbol that was previously hidden. - ReportError(RudeEditKind.Delete) - Return - - Case SyntaxKind.ConstructorBlock - ' Allow deletion of a parameterless constructor. - ' Semantic analysis reports an error if the parameterless ctor isn't replaced by a default ctor. - If Not SyntaxUtilities.IsParameterlessConstructor(oldNode) Then - ReportError(RudeEditKind.Delete) - End If - + SyntaxKind.DeclareFunctionStatement, + SyntaxKind.ConstructorBlock + ' We do not report member delete here since the member might be moving to a different part of a partial type declaration. + ' If that is not the case the semantic analysis reports the rude edit. Return Case SyntaxKind.GetAccessorBlock, SyntaxKind.SetAccessorBlock, SyntaxKind.AddHandlerAccessorBlock, SyntaxKind.RemoveHandlerAccessorBlock, - SyntaxKind.RaiseEventAccessorBlock - ' An accessor can be removed. Accessors are not hiding other symbols. - ' If the new compilation still uses the removed accessor a semantic error will be reported. - ' For simplicity though we disallow deletion of accessors for now. - ReportError(RudeEditKind.Delete) + SyntaxKind.RaiseEventAccessorBlock, + SyntaxKind.EnumMemberDeclaration + ' We do not report error here since it will be reported in semantic analysis. Return Case SyntaxKind.AttributeList, @@ -2186,12 +2208,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Delete) Return - Case SyntaxKind.EnumMemberDeclaration - ' We could allow removing enum member if it didn't affect the values of other enum members. - ' If the updated compilation binds without errors it means that the enum value wasn't used. - ReportError(RudeEditKind.Delete) - Return - Case SyntaxKind.TypeParameter, SyntaxKind.TypeParameterList, SyntaxKind.Parameter, @@ -2400,13 +2416,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - If Not SyntaxFactory.AreEquivalent(oldNode.Modifiers, newNode.Modifiers) Then + If Not AreModifiersEquivalent(oldNode.Modifiers, newNode.Modifiers, ignore:=SyntaxKind.PartialKeyword) Then ReportError(RudeEditKind.ModifiersUpdate) Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - ReportError(RudeEditKind.Renamed) + If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then + ReportError(RudeEditKind.Renamed) + End If End Sub Private Sub ClassifyUpdate(oldNode As TypeBlockSyntax, newNode As TypeBlockSyntax) @@ -2776,6 +2793,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ClassifyUpdate(oldNode.Identifier, newNode.Identifier) End Sub + Private Shared Function AreModifiersEquivalent(oldModifiers As SyntaxTokenList, newModifiers As SyntaxTokenList, ignore As SyntaxKind) As Boolean + Dim oldIgnoredModifierIndex = oldModifiers.IndexOf(ignore) + Dim newIgnoredModifierIndex = newModifiers.IndexOf(ignore) + + If oldIgnoredModifierIndex >= 0 Then + oldModifiers = oldModifiers.RemoveAt(oldIgnoredModifierIndex) + End If + + If newIgnoredModifierIndex >= 0 Then + newModifiers = newModifiers.RemoveAt(newIgnoredModifierIndex) + End If + + Return SyntaxFactory.AreEquivalent(oldModifiers, newModifiers) + End Function + Private Sub ClassifyMethodBodyRudeUpdate(oldBody As MethodBlockBaseSyntax, newBody As MethodBlockBaseSyntax, containingMethod As MethodBlockSyntax, @@ -2832,10 +2864,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue #End Region End Structure - Friend Overrides Sub ReportTopLevelSyntacticRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), - match As Match(Of SyntaxNode), - edit As Edit(Of SyntaxNode), - editMap As Dictionary(Of SyntaxNode, EditKind)) + Friend Overrides Sub ReportTopLevelSyntacticRudeEdits( + diagnostics As ArrayBuilder(Of RudeEditDiagnostic), + match As Match(Of SyntaxNode), + edit As Edit(Of SyntaxNode), + editMap As Dictionary(Of SyntaxNode, EditKind)) ' For most nodes we ignore Insert and Delete edits if their parent was also inserted or deleted, respectively. ' For ModifiedIdentifiers though we check the grandparent instead because variables can move across @@ -2880,27 +2913,146 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue #End Region #Region "Semantic Rude Edits" - Friend Overrides Sub ReportInsertedMemberSymbolRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), newSymbol As ISymbol) - ' CLR doesn't support adding P/Invokes. - - ' VB needs to check if the type doesn't contain methods with DllImport attribute. - If newSymbol.IsKind(SymbolKind.NamedType) Then - For Each member In DirectCast(newSymbol, INamedTypeSymbol).GetMembers() - ReportDllImportInsertRudeEdit(diagnostics, member) - Next - Else - ReportDllImportInsertRudeEdit(diagnostics, newSymbol) + Friend Overrides Sub ReportInsertedMemberSymbolRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), newSymbol As ISymbol, newNode As SyntaxNode, insertingIntoExistingContainingType As Boolean) + Dim kind = GetInsertedMemberSymbolRudeEditKind(newSymbol, insertingIntoExistingContainingType) + + If kind <> RudeEditKind.None Then + diagnostics.Add(New RudeEditDiagnostic( + kind, + GetDiagnosticSpan(newNode, EditKind.Insert), + newNode, + arguments:={GetDisplayName(newNode, EditKind.Insert)})) End If End Sub - Private Shared Sub ReportDllImportInsertRudeEdit(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), member As ISymbol) - If member.IsKind(SymbolKind.Method) AndAlso - DirectCast(member, IMethodSymbol).GetDllImportData() IsNot Nothing Then + Private Shared Function GetInsertedMemberSymbolRudeEditKind(newSymbol As ISymbol, insertingIntoExistingContainingType As Boolean) As RudeEditKind + Select Case newSymbol.Kind + Case SymbolKind.Method + Dim method = DirectCast(newSymbol, IMethodSymbol) - diagnostics.Add(New RudeEditDiagnostic(RudeEditKind.InsertDllImport, - member.Locations.First().SourceSpan)) + ' Inserting P/Invoke into a new or existing type is not allowed. + If method.GetDllImportData() IsNot Nothing Then + Return RudeEditKind.InsertDllImport + End If + + ' Inserting method with handles clause into a new or existing type is not allowed. + If Not method.HandledEvents.IsEmpty Then + Return RudeEditKind.InsertHandlesClause + End If + + Case SymbolKind.NamedType + Dim type = CType(newSymbol, INamedTypeSymbol) + + ' Inserting module is not allowed. + If type.TypeKind = TypeKind.Module Then + Return RudeEditKind.Insert + End If + End Select + + ' All rude edits below only apply when inserting into an existing type (not when the type itself is inserted): + If Not insertingIntoExistingContainingType Then + Return RudeEditKind.None + End If + + If newSymbol.ContainingType.Arity > 0 AndAlso newSymbol.Kind <> SymbolKind.NamedType Then + Return RudeEditKind.InsertIntoGenericType + End If + + ' Inserting virtual or interface member is not allowed. + If (newSymbol.IsVirtual Or newSymbol.IsOverride Or newSymbol.IsAbstract) AndAlso newSymbol.Kind <> SymbolKind.NamedType Then + Return RudeEditKind.InsertVirtual End If + + Select Case newSymbol.Kind + Case SymbolKind.Method + Dim method = DirectCast(newSymbol, IMethodSymbol) + + ' Inserting generic method into an existing type is not allowed. + If method.Arity > 0 Then + Return RudeEditKind.InsertGenericMethod + End If + + ' Inserting operator to an existing type is not allowed. + If method.MethodKind = MethodKind.Conversion OrElse method.MethodKind = MethodKind.UserDefinedOperator Then + Return RudeEditKind.InsertOperator + End If + + Return RudeEditKind.None + + Case SymbolKind.Field + 'Dim field = DirectCast(newSymbol, IFieldSymbol) + ' TODO: + ' Can't insert WithEvents field since it is effectively a virtual property. + ' WithEvents X As C + 'If field.Modifiers.Any(SyntaxKind.WithEventsKeyword) Then + ' ReportError(RudeEditKind.Insert) + ' Return + 'End If + + 'Dim containingType = field.Parent + 'If containingType.IsKind(SyntaxKind.ModuleBlock) Then + ' ReportError(RudeEditKind.Insert) + ' Return + 'End If + Return RudeEditKind.None + End Select + + Return RudeEditKind.None + End Function + + Friend Overrides Sub ReportTypeDeclarationInsertDeleteRudeEdits(diagnostics As ArrayBuilder(Of RudeEditDiagnostic), oldType As INamedTypeSymbol, newType As INamedTypeSymbol, newDeclaration As SyntaxNode, cancellationToken As CancellationToken) + Dim oldNodes = ArrayBuilder(Of SyntaxNode).GetInstance() + Dim newNodes = ArrayBuilder(Of SyntaxNode).GetInstance() + + Dim report = + Sub(addNodes As Action(Of ArrayBuilder(Of SyntaxNode), TypeBlockSyntax), rudeEditKind As RudeEditKind) + + For Each syntaxRef In oldType.DeclaringSyntaxReferences + addNodes(oldNodes, CType(GetSymbolDeclarationSyntax(syntaxRef, cancellationToken), TypeBlockSyntax)) + Next + + For Each syntaxRef In newType.DeclaringSyntaxReferences + addNodes(newNodes, CType(GetSymbolDeclarationSyntax(syntaxRef, cancellationToken), TypeBlockSyntax)) + Next + + If oldNodes.Count <> newNodes.Count OrElse + oldNodes.Zip(newNodes, Function(oldNode, newNode) SyntaxFactory.AreEquivalent(oldNode, newNode)).Any(Function(isEquivalent) Not isEquivalent) Then + + diagnostics.Add(New RudeEditDiagnostic( + rudeEditKind, + GetDiagnosticSpan(newDeclaration, EditKind.Update), + newDeclaration, + arguments:={GetDisplayName(newDeclaration, EditKind.Update)})) + End If + + oldNodes.Clear() + newNodes.Clear() + End Sub + + ' Consider better error messages + report(Sub(b, t) AddNodes(b, t.BlockStatement.AttributeLists), RudeEditKind.Update) + report(Sub(b, t) AddNodes(b, t.BlockStatement.TypeParameterList?.Parameters), RudeEditKind.Update) + + report(Sub(b, t) + For Each inherit In t.Inherits + For Each baseType In inherit.Types + b.Add(baseType) + Next + Next + End Sub, RudeEditKind.BaseTypeOrInterfaceUpdate) + + report(Sub(b, t) + For Each impl In t.Implements + For Each baseInterface In impl.Types + b.Add(baseInterface) + Next + Next + End Sub, RudeEditKind.BaseTypeOrInterfaceUpdate) + + oldNodes.Free() + newNodes.Free() End Sub + #End Region #Region "Exception Handling Rude Edits" diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index b15e103c6496e..c96f2c59e3e89 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -1252,4 +1252,7 @@ Sub(<parameterList>) <statement> ({0} Events) + + Shared constructor + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf index b9e2973e5caaa..0beb96dd6f5e9 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.cs.xlf @@ -102,6 +102,11 @@ Odebrat klíčové slovo Shared ze členu modulu {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Sem vložte název, kterým deklarujete novou položku. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf index 5c485230d8b86..68737b6962a3a 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.de.xlf @@ -102,6 +102,11 @@ Schlüsselwort "Shared" aus Modulmember entfernen {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Geben Sie hier einen Namen ein, um ein neues Feld zu deklarieren. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf index 9b4cc118bfca2..406ae78325ba2 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.es.xlf @@ -102,6 +102,11 @@ Quitar la palabra clave "Shared" del miembro del módulo {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Escriba aquí un nombre para declarar un nuevo campo. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf index 8c1a5feddf8cd..9a0f1ac27cd59 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.fr.xlf @@ -102,6 +102,11 @@ Supprimer le mot clé 'Shared' du membre de module {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Tapez un nom ici pour déclarer un nouveau champ. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf index 33d4cb185b466..d08b361d3c59f 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.it.xlf @@ -102,6 +102,11 @@ Rimuovi la parola chiave 'Shared' dal membro Module {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Digitare qui un nome per dichiarare un nuovo campo. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf index d085f938ebdbc..58bd3c15ec8ac 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ja.xlf @@ -102,6 +102,11 @@ モジュール メンバーから 'Shared' キーワードを削除 {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. 新しいフィールドを宣言するには、ここに名前を入力します。 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf index 3c56f2126e73a..14dc33ccf67ca 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ko.xlf @@ -102,6 +102,11 @@ 모듈 멤버에서 'Shared' 키워드 제거 {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. 여기에 이름을 입력하여 새 필드를 선언하세요. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf index 8464d256ffded..cb61d441eb216 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pl.xlf @@ -102,6 +102,11 @@ Usuń słowo kluczowe "Shared" ze składowej modułu {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Wpisz tutaj nazwę, aby utworzyć deklarację nowego pola. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf index 4056616e3f37e..8be8d14a2064a 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.pt-BR.xlf @@ -102,6 +102,11 @@ Remover a palavra-chave 'Shared' do membro do Módulo {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Digite um nome aqui para declarar um novo campo. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf index 44e240371bfc2..eac901e155ddf 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.ru.xlf @@ -102,6 +102,11 @@ Удалить ключевое слово "Shared" из элемента модуля {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Введите здесь имя для объявления нового поля. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf index d0bec07409824..883138c04a59f 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.tr.xlf @@ -102,6 +102,11 @@ Modül üyesinden 'Shared' anahtar sözcüğünü kaldır {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. Yeni bir alan bildirmek için buraya bir ad yazın. diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf index 7120b1e4c0645..a6773ed214ba0 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hans.xlf @@ -102,6 +102,11 @@ 从模块成员中删除 "Shared" 关键字 {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. 请在此处键入名称以声明新字段。 diff --git a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf index 6778c2f3d784d..483e284ff7309 100644 --- a/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf +++ b/src/Features/VisualBasic/Portable/xlf/VBFeaturesResources.zh-Hant.xlf @@ -102,6 +102,11 @@ 從模組成員移除 'Shared' 關鍵字 {Locked="Shared"} "Shared" is a VB keyword and should not be localized. + + Shared constructor + Shared constructor + + Type a name here to declare a new field. 於此輸入名稱以宣告新的欄位。 diff --git a/src/Workspaces/Core/Portable/Differencing/Match.cs b/src/Workspaces/Core/Portable/Differencing/Match.cs index 1f4fbf8202d80..e4a8e07a730a6 100644 --- a/src/Workspaces/Core/Portable/Differencing/Match.cs +++ b/src/Workspaces/Core/Portable/Differencing/Match.cs @@ -210,18 +210,31 @@ private void ComputeMatchForLabel(List s1, List s2, int tiedToAnce // consider avoiding matching them to all other nodes of the same label. // Rather we should only match them with their siblings that share the same parent. - var ancestor1 = _comparer.GetAncestor(node1, tiedToAncestor); - var ancestor2 = _comparer.GetAncestor(node2, tiedToAncestor); - - // Since CategorizeNodesByLabels added nodes to the s1/s2 lists in depth-first prefix order, - // we can also accept equality in the following condition. That's because we find the partner - // of the parent node before we get to finding it for the child node of the same kind. - Debug.Assert(_comparer.GetLabel(ancestor1) <= _comparer.GetLabel(node1)); - - if (!Contains(ancestor1, ancestor2)) + // Check if nodes that are configured to be tied to their ancestor have the respective ancestor matching. + // In cases when we compare substrees rooted below both of these ancestors we assume the ancestors are + // matching since the roots of the subtrees must match and therefore their ancestors must match as well. + // If one node's ancestor is present in the subtree and the other isn't then we are not in the scenario + // of comparing subtrees with matching roots and thus we consider the nodes not matching. + + var hasAncestor1 = _comparer.TryGetAncestor(node1, tiedToAncestor, out var ancestor1); + var hasAncestor2 = _comparer.TryGetAncestor(node2, tiedToAncestor, out var ancestor2); + if (hasAncestor1 != hasAncestor2) { continue; } + + if (hasAncestor1) + { + // Since CategorizeNodesByLabels added nodes to the s1/s2 lists in depth-first prefix order, + // we can also accept equality in the following condition. That's because we find the partner + // of the parent node before we get to finding it for the child node of the same kind. + Debug.Assert(_comparer.GetLabel(ancestor1) <= _comparer.GetLabel(node1)); + + if (!Contains(ancestor1, ancestor2)) + { + continue; + } + } } // We know that diff --git a/src/Workspaces/Core/Portable/Differencing/TreeComparer.cs b/src/Workspaces/Core/Portable/Differencing/TreeComparer.cs index 876e78517fa5e..a0ba580f468cc 100644 --- a/src/Workspaces/Core/Portable/Differencing/TreeComparer.cs +++ b/src/Workspaces/Core/Portable/Differencing/TreeComparer.cs @@ -100,15 +100,25 @@ internal TNode GetParent(TNode node) return parent!; } - internal TNode GetAncestor(TNode node, int level) + internal bool TryGetAncestor(TNode node, int level, [MaybeNullWhen(false)] out TNode ancestor) { while (level > 0) { - node = GetParent(node); + if (TryGetParent(node, out var parent)) + { + node = parent; + } + else + { + ancestor = default; + return false; + } + level--; } - return node; + ancestor = node; + return true; } /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs index 5bce6f2cb684b..be5b7d56e52ea 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledHashSet.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Roslyn.Utilities; + namespace Microsoft.CodeAnalysis.PooledObjects { - internal partial class PooledHashSet : IPooled + internal partial class PooledHashSet : IPooled, IReadOnlySet { public static PooledDisposer> GetInstance(out PooledHashSet instance) {