From fcb7bd3984b56fa82d8b8b72258ef1709581dd6a Mon Sep 17 00:00:00 2001 From: David Wengier Date: Mon, 10 Oct 2022 16:17:27 +1100 Subject: [PATCH 1/2] Allow reordering parameters, and fix inserting, and deleting them --- .../EditAndContinue/EditAndContinueTests.cs | 132 +++++++++++++++ .../EditAndContinue/TopLevelEditingTests.cs | 156 +++++++++++++++++- .../EditAndContinue/TopLevelEditingTests.vb | 14 +- .../CSharpEditAndContinueAnalyzer.cs | 22 ++- .../AbstractEditAndContinueAnalyzer.cs | 56 +++++-- .../VisualBasicEditAndContinueAnalyzer.vb | 25 ++- 6 files changed, 378 insertions(+), 27 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index dd46ff9d2e4eb..a1fe44006d1e5 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -15698,6 +15698,138 @@ .maxstack 8 .Verify(); } + [Fact] + public void Method_InsertAndDeleteParameter() + { + using var _ = new EditAndContinueTest(options: TestOptions.DebugDll, targetFramework: TargetFramework.NetStandard20) + .AddGeneration( + source: $$""" + class C + { + void M(int someInt) { someInt.ToString(); } + } + """, + validator: g => + { + g.VerifyTypeDefNames("", "C"); + g.VerifyMethodDefNames("M", ".ctor"); + }) + .AddGeneration( + source: $$""" + class C + { + void M(int someInt, bool someBool) { someInt.ToString(); } + } + """, + edits: new[] { + Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")), + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol), + }, + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyMethodDefNames("M", "M"); + g.VerifyDeletedMembers("C: {M}"); + + g.VerifyEncLogDefinitions(new[] + { + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddMethod), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.AddParameter), + Row(2, TableIndex.Param, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.AddParameter), + Row(3, TableIndex.Param, EditAndContinueOperation.Default) + }); + g.VerifyEncMapDefinitions(new[] + { + Handle(1, TableIndex.MethodDef), + Handle(3, TableIndex.MethodDef), + Handle(1, TableIndex.Param), + Handle(2, TableIndex.Param), + Handle(3, TableIndex.Param) + }); + + var expectedIL = """ + { + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj 0x0A000006 + IL_0005: throw + } + { + // Code size 10 (0xa) + .maxstack 8 + IL_0000: nop + IL_0001: ldarga.s V_1 + IL_0003: call 0x0A000007 + IL_0008: pop + IL_0009: ret + } + """; + + // Can't verify the IL of individual methods because that requires IMethodSymbolInternal implementations + g.VerifyIL(expectedIL); + }) + .AddGeneration( + source: $$""" + class C + { + void M(int someInt) { someInt.ToString(); } + } + """, + edits: new[] { + Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")), + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol), + }, + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyMethodDefNames("M", "M"); + g.VerifyDeletedMembers("C: {M}"); + + g.VerifyEncLogDefinitions(new[] + { + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(2, TableIndex.Param, EditAndContinueOperation.Default), + Row(3, TableIndex.Param, EditAndContinueOperation.Default) + }); + g.VerifyEncMapDefinitions(new[] + { + Handle(1, TableIndex.MethodDef), + Handle(3, TableIndex.MethodDef), + Handle(1, TableIndex.Param), + Handle(2, TableIndex.Param), + Handle(3, TableIndex.Param) + }); + + var expectedIL = """ + { + // Code size 10 (0xa) + .maxstack 8 + IL_0000: nop + IL_0001: ldarga.s V_1 + IL_0003: call 0x0A000008 + IL_0008: pop + IL_0009: ret + } + { + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj 0x0A000009 + IL_0005: throw + } + """; + + // Can't verify the IL of individual methods because that requires IMethodSymbolInternal implementations + g.VerifyIL(expectedIL); + }) + .Verify(); + } + [Fact] public void FileTypes_01() { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index e5855d74ab839..3169948fdb132 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -2823,7 +2823,9 @@ protected C(C other) new[] { Diagnostic(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, "protected virtual bool PrintMembers(System.Text.StringBuilder sb)", "PrintMembers(System.Text.StringBuilder builder)"), + Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "System.Text.StringBuilder sb", FeaturesResources.parameter), Diagnostic(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, "public virtual bool Equals(C rhs)", "Equals(C other)"), + Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "C rhs", FeaturesResources.parameter), Diagnostic(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, "protected C(C other)", "C(C original)") }, capabilities: EditAndContinueCapabilities.Baseline); @@ -6907,6 +6909,27 @@ public void Method_ReturnType_Update_RuntimeTypeChanged(string oldType, string n capabilities: EditAndContinueCapabilities.Baseline); } + [Fact] + public void Method_ReturnType_Update_AndBodyChange() + { + var src1 = "class C { int M() => 1; }"; + var src2 = "class C { char M() => 'a'; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M")) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "char M()", FeaturesResources.method) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + [Fact] public void Method_Update() { @@ -16606,12 +16629,23 @@ event Action F { add {} remove {} } [Fact] public void ParameterRename_Method1() { - var src1 = @"class C { public void M(int a) {} }"; - var src2 = @"class C { public void M(int b) {} } "; + var src1 = @"class C { public void M(int a) { a.ToString(); } }"; + var src2 = @"class C { public void M(int b) { b.ToString(); } } "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( + "Update [public void M(int a) { a.ToString(); }]@10 -> [public void M(int b) { b.ToString(); }]@10", "Update [int a]@24 -> [int b]@24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) + }, + capabilities: EditAndContinueCapabilities.UpdateParameters); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] @@ -16662,11 +16696,19 @@ public void ParameterRename_Indexer2() public void ParameterInsert1() { var src1 = @"class C { public void M() {} }"; - var src2 = @"class C { public void M(int a) {} } "; + var src2 = @"class C { public void M(int a) { a.ToString(); } } "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( + "Update [public void M() {}]@10 -> [public void M(int a) { a.ToString(); }]@10", "Insert [int a]@24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } [Fact] @@ -16679,6 +16721,32 @@ public void ParameterInsert2() edits.VerifyEdits( "Update [(int a)]@23 -> [(int a, ref int b)]@23", "Insert [ref int b]@31"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + } + + [Fact] + public void ParameterInsert3() + { + var src1 = @"class C { public void M() {} }"; + var src2 = @"class C { public int M(int a) { return a; } } "; + + var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( + "Update [public void M() {}]@10 -> [public int M(int a) { return a; }]@10", + "Insert [int a]@23"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } [Fact] @@ -16690,6 +16758,13 @@ public void ParameterDelete1() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( "Delete [int a]@24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } [Fact] @@ -16702,6 +16777,13 @@ public void ParameterDelete2() edits.VerifyEdits( "Update [(int a, int b)]@23 -> [(int b)]@23", "Delete [int a]@24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } [Fact] @@ -16724,10 +16806,65 @@ public void ParameterReorder() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( "Reorder [int b]@31 -> @24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) + }, + capabilities: EditAndContinueCapabilities.UpdateParameters); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] public void ParameterReorderAndUpdate() + { + var src1 = @"class C { public void M(int a, int b) { a.ToString(); } }"; + var src2 = @"class C { public void M(int b, int a) { b.ToString(); } } "; + + var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( + "Update [public void M(int a, int b) { a.ToString(); }]@10 -> [public void M(int b, int a) { b.ToString(); }]@10", + "Reorder [int b]@31 -> @24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) + }, + capabilities: EditAndContinueCapabilities.UpdateParameters); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void ParameterReorderAndChangeTypes() + { + var src1 = @"class C { public void M(string a, int b) { a.ToString(); } }"; + var src2 = @"class C { public void M(int b, string a) { b.ToString(); } } "; + + var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( + "Update [public void M(string a, int b) { a.ToString(); }]@10 -> [public void M(int b, string a) { b.ToString(); }]@10", + "Reorder [int b]@34 -> @24"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_String)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Int32)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void ParameterReorderAndRename() { var src1 = @"class C { public void M(int a, int b) {} }"; var src2 = @"class C { public void M(int b, int c) {} } "; @@ -16736,6 +16873,19 @@ public void ParameterReorderAndUpdate() edits.VerifyEdits( "Reorder [int b]@31 -> @24", "Update [int a]@24 -> [int c]@31"); + + edits.VerifySemantics( + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) + }, + capabilities: EditAndContinueCapabilities.UpdateParameters); + + edits.VerifySemanticDiagnostics( + new[] { + Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter), + Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int c", FeaturesResources.parameter) + }, + capabilities: EditAndContinueCapabilities.Baseline); } [Theory] diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index c70c03518a2ee..9a4b7b6cef80b 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -10760,8 +10760,16 @@ End Class edits.VerifyEdits( "Reorder [b As Integer]@38 -> @24") + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.M")) + }, + capabilities:=EditAndContinueCapabilities.UpdateParameters) + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Move, "b As Integer", FeaturesResources.parameter)) + {Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "b As Integer", FeaturesResources.parameter)}, + capabilities:=EditAndContinueCapabilities.Baseline) End Sub @@ -10775,8 +10783,8 @@ End Class "Update [a]@24 -> [c]@38") edits.VerifySemanticDiagnostics( - {Diagnostic(RudeEditKind.Move, "b As Integer", FeaturesResources.parameter), - Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "c", FeaturesResources.parameter)}, + {Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "b As Integer", FeaturesResources.parameter), + Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "c", FeaturesResources.parameter)}, capabilities:=EditAndContinueCapabilities.Baseline) End Sub diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index a1a4ef1fa0474..d487f158c1c1d 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1250,11 +1250,32 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen IReadOnlyDictionary editMap, CancellationToken cancellationToken) { + if (editKind == EditKind.Reorder && + oldNode is not ParameterSyntax && + newNode is not ParameterSyntax) + { + // Other than parameters, 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). + + // 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(oldNode!) && !IsDeclarationWithInitializer(newNode!)); + return OneOrMany<(ISymbol?, ISymbol?, EditKind)>.Empty; + } + var oldSymbol = (oldNode != null) ? GetSymbolForEdit(oldNode, oldModel!, cancellationToken) : null; var newSymbol = (newNode != null) ? GetSymbolForEdit(newNode, newModel, cancellationToken) : null; switch (editKind) { + case EditKind.Reorder: + Debug.Assert(oldSymbol is IParameterSymbol); + Debug.Assert(newSymbol is IParameterSymbol); + + // When parameters are reordered, we issue an update edit for the containing method + return new OneOrMany<(ISymbol?, ISymbol?, EditKind)>((oldSymbol.ContainingSymbol, newSymbol.ContainingSymbol, EditKind.Update)); case EditKind.Update: Contract.ThrowIfNull(oldNode); Contract.ThrowIfNull(newNode); @@ -2330,7 +2351,6 @@ private void ClassifyReorder(SyntaxNode newNode) return; case SyntaxKind.TypeParameter: - case SyntaxKind.Parameter: ReportError(RudeEditKind.Move); return; } diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 0976584cc372e..94d0694611fd1 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2396,19 +2396,6 @@ private async Task> AnalyzeSemanticsAsync( { cancellationToken.ThrowIfCancellationRequested(); - if (edit.Kind == 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). - - // 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; - } - Debug.Assert(edit.OldNode is null || edit.NewNode is null || IsNamespaceDeclaration(edit.OldNode) == IsNamespaceDeclaration(edit.NewNode)); // We can ignore namespace nodes in newly added documents (old model is not available) since @@ -3066,10 +3053,45 @@ private async Task> AnalyzeSemanticsAsync( } // For renames where the symbol allows deletion, we don't create an update edit, we create a delete - // and an add. During emit an empty body will be created for the old name. Sometimes - // when members are moved between documents in partial classes, they can appear as renames, so - // we also check that the old symbol can't be resolved in the new compilation - if (oldSymbol.Name != newSymbol.Name && + // and an add. During emit an empty body will be created for the old name. + var createDeleteAndInsertEdits = oldSymbol.Name != newSymbol.Name; + + // When a methods parameters are reordered or there is an insert or an add, we need to handle things differently + if (oldSymbol is IMethodSymbol oldMethod && + newSymbol is IMethodSymbol newMethod) + { + // For inserts and deletes, the edits for the parameter itself will do the work + if (oldMethod.Parameters.Length != newMethod.Parameters.Length) + { + continue; + } + + // For reordering or parameters we need to report insert and delete edits, but we also need to account for + // renames if the runtime doesn't support it. We track this with a syntax node that we can use to report + // the rude edit. + IParameterSymbol? renamedParameter = null; + for (var i = 0; i < oldMethod.Parameters.Length; i++) + { + var rudeEditKind = RudeEditKind.None; + var hasParameterTypeChange = false; + var unused = false; + AnalyzeParameterType(oldMethod.Parameters[i], newMethod.Parameters[i], capabilities, ref rudeEditKind, ref unused, ref hasParameterTypeChange, cancellationToken); + + createDeleteAndInsertEdits |= hasParameterTypeChange; + renamedParameter ??= oldMethod.Parameters[i].Name != newMethod.Parameters[i].Name ? newMethod.Parameters[i] : null; + } + + if (!createDeleteAndInsertEdits && renamedParameter is not null && !capabilities.Grant(EditAndContinueCapabilities.UpdateParameters)) + { + processedSymbols.Add(renamedParameter); + ReportUpdateRudeEdit(diagnostics, RudeEditKind.RenamingNotSupportedByRuntime, renamedParameter, GetRudeEditDiagnosticNode(renamedParameter, cancellationToken), cancellationToken); + continue; + } + } + + // Sometimes when members are moved between documents in partial classes, they can appear as renames, + // so we also check that the old symbol can't be resolved in the new compilation + if (createDeleteAndInsertEdits && AllowsDeletion(oldSymbol) && CanAddNewMember(oldSymbol, capabilities, cancellationToken) && SymbolKey.Create(oldSymbol, cancellationToken).Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol is null) diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index a7f180f98b0de..bd80f56145a0b 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -1270,6 +1270,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Dim newSymbols As OneOrMany(Of ISymbol) = Nothing Select Case editKind + Case EditKind.Reorder + If TryCast(oldNode, ParameterSyntax) Is Nothing OrElse TryCast(newNode, ParameterSyntax) Is Nothing Then + ' Other than parameters, 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). + + ' 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(Not IsDeclarationWithInitializer(oldNode) AndAlso Not IsDeclarationWithInitializer(newNode)) + Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty + End If + + If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) OrElse + Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then + Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty + End If + + Return OneOrMany.Create((oldSymbols(0).ContainingSymbol, newSymbols(0).ContainingSymbol, EditKind.Update)) Case EditKind.Delete If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) Then Return OneOrMany(Of (ISymbol, ISymbol, EditKind)).Empty @@ -2233,7 +2252,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.NewConstraint, SyntaxKind.TypeConstraint, SyntaxKind.AttributeList, - SyntaxKind.Attribute + SyntaxKind.Attribute, + SyntaxKind.Parameter ' We'll ignore these edits. A general policy is to ignore edits that are only discoverable via reflection. Return @@ -2258,8 +2278,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ReportError(RudeEditKind.Move) Return - Case SyntaxKind.TypeParameter, - SyntaxKind.Parameter + Case SyntaxKind.TypeParameter ReportError(RudeEditKind.Move) Return From 3176474899a665676df4c684846de9f9e5374f64 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Fri, 11 Nov 2022 14:59:57 +1100 Subject: [PATCH 2/2] Update src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs --- .../Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 94d0694611fd1..1f194ff473eba 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -3066,7 +3066,7 @@ private async Task> AnalyzeSemanticsAsync( continue; } - // For reordering or parameters we need to report insert and delete edits, but we also need to account for + // For reordering of parameters we need to report insert and delete edits, but we also need to account for // renames if the runtime doesn't support it. We track this with a syntax node that we can use to report // the rude edit. IParameterSymbol? renamedParameter = null;