From 8cfddbfc9d60d6873be286fb7c13364f9bc7cd57 Mon Sep 17 00:00:00 2001 From: David Wengier Date: Wed, 10 Nov 2021 10:05:10 +1100 Subject: [PATCH] Compare arrow expression clauses when they are children of properties or indexers (#57624) --- .../ActiveStatementTests.Methods.cs | 46 +++++++++++++++++++ .../EditAndContinue/TopLevelEditingTests.cs | 45 +++++++++++++----- .../EditAndContinue/SyntaxComparer.cs | 11 +++++ 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs index 422c7a34e5536..25fe14be271cc 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.Methods.cs @@ -560,6 +560,29 @@ class C Diagnostic(RudeEditKind.DeleteActiveStatement, "get", FeaturesResources.code)); } + [Fact] + public void Property_ExpressionBody_NonLeaf() + { + var src1 = @" +class C +{ + int P => M(); + int M() { return 1; } +} +"; + var src2 = @" +class C +{ + int P => M(); + int M() { return 2; } +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void Property_BlockBodyToExpressionBody1() { @@ -664,6 +687,29 @@ public void Indexer_BlockBodyToExpressionBody2() Diagnostic(RudeEditKind.Delete, "int this[int a]", DeletedSymbolDisplay(CSharpFeaturesResources.indexer_setter, "this[int a].set"))); } + [Fact] + public void Indexer_ExpressionBody_NonLeaf() + { + var src1 = @" +class C +{ + int this[int index] => M(); + int M() { return 1; } +} +"; + var src2 = @" +class C +{ + int this[int index] => M(); + int M() { return 2; } +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void Update_Leaf_Indexers1() { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index dca9939939b67..1b69ecd483209 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -12601,7 +12601,9 @@ public void Property_ExpressionBody_Update() var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Update [int P => 1;]@10 -> [int P => 2;]@10"); + edits.VerifyEdits( + "Update [int P => 1;]@10 -> [int P => 2;]@10", + "Update [=> 1]@16 -> [=> 2]@16"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -12633,7 +12635,8 @@ public void Property_ExpressionBodyToBlockBody1() edits.VerifyEdits( "Update [int P => 1;]@10 -> [int P { get { return 2; } }]@10", "Insert [{ get { return 2; } }]@16", - "Insert [get { return 2; }]@18"); + "Insert [get { return 2; }]@18", + "Delete [=> 1]@16"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -12653,7 +12656,8 @@ public void Property_ExpressionBodyToBlockBody2() "Update [int P => 1;]@10 -> [int P { get { return 2; } set { } }]@10", "Insert [{ get { return 2; } set { } }]@16", "Insert [get { return 2; }]@18", - "Insert [set { }]@36"); + "Insert [set { }]@36", + "Delete [=> 1]@16"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -12672,6 +12676,7 @@ public void Property_BlockBodyToExpressionBody1() edits.VerifyEdits( "Update [int P { get { return 2; } }]@10 -> [int P => 1;]@10", + "Insert [=> 1]@16", "Delete [{ get { return 2; } }]@16", "Delete [get { return 2; }]@18"); @@ -12691,6 +12696,7 @@ public void Property_BlockBodyToExpressionBody2() edits.VerifyEdits( "Update [int P { get { return 2; } set { } }]@10 -> [int P => 1;]@10", + "Insert [=> 1]@16", "Delete [{ get { return 2; } set { } }]@16", "Delete [get { return 2; }]@18", "Delete [set { }]@36"); @@ -12709,7 +12715,8 @@ public void Property_ExpressionBodyToGetterExpressionBody() edits.VerifyEdits( "Update [int P => 1;]@10 -> [int P { get => 2; }]@10", "Insert [{ get => 2; }]@16", - "Insert [get => 2;]@18"); + "Insert [get => 2;]@18", + "Delete [=> 1]@16"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -12726,6 +12733,7 @@ public void Property_GetterExpressionBodyToExpressionBody() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( "Update [int P { get => 2; }]@10 -> [int P => 1;]@10", + "Insert [=> 1]@16", "Delete [{ get => 2; }]@16", "Delete [get => 2;]@18"); @@ -13697,7 +13705,8 @@ public void IndexerWithExpressionBody_Update() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int this[int a] => 1;]@10 -> [int this[int a] => 2;]@10"); + "Update [int this[int a] => 1;]@10 -> [int this[int a] => 2;]@10", + "Update [=> 1]@26 -> [=> 2]@26"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -13727,7 +13736,8 @@ class C 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"); + "Update [int this[int a] => new Func(() => a + 1)() + 10;]@35 -> [int this[int a] => new Func(() => 2)() + 11;]@35", + "Update [=> new Func(() => a + 1)() + 10]@51 -> [=> new Func(() => 2)() + 11]@51"); edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")); @@ -13755,7 +13765,8 @@ class C var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int this[int a] => new Func(() => a + 1)();]@35 -> [int this[int a] => new Func(() => 2)();]@35"); + "Update [int this[int a] => new Func(() => a + 1)();]@35 -> [int this[int a] => new Func(() => 2)();]@35", + "Update [=> new Func(() => a + 1)()]@51 -> [=> new Func(() => 2)()]@51"); edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")); @@ -13783,7 +13794,8 @@ class C var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int this[int a] => new Func(() => { return a + 1; })();]@35 -> [int this[int a] => new Func(() => { return 2; })();]@35"); + "Update [int this[int a] => new Func(() => { return a + 1; })();]@35 -> [int this[int a] => new Func(() => { return 2; })();]@35", + "Update [=> new Func(() => { return a + 1; })()]@51 -> [=> new Func(() => { return 2; })()]@51"); edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")); @@ -13811,7 +13823,8 @@ class C var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [int this[int a] => new Func(delegate { return a + 1; })();]@35 -> [int this[int a] => new Func(delegate { return 2; })();]@35"); + "Update [int this[int a] => new Func(delegate { return a + 1; })();]@35 -> [int this[int a] => new Func(delegate { return 2; })();]@35", + "Update [=> new Func(delegate { return a + 1; })()]@51 -> [=> new Func(delegate { return 2; })()]@51"); edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.NotCapturingVariable, "a", "a")); @@ -13828,7 +13841,8 @@ public void Indexer_ExpressionBodyToBlockBody() edits.VerifyEdits( "Update [int this[int a] => 1;]@10 -> [int this[int a] { get { return 1; } }]@10", "Insert [{ get { return 1; } }]@26", - "Insert [get { return 1; }]@28"); + "Insert [get { return 1; }]@28", + "Delete [=> 1]@26"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -13846,6 +13860,7 @@ public void Indexer_BlockBodyToExpressionBody() edits.VerifyEdits( "Update [int this[int a] { get { return 1; } }]@10 -> [int this[int a] => 1;]@10", + "Insert [=> 1]@26", "Delete [{ get { return 1; } }]@26", "Delete [get { return 1; }]@28"); @@ -13892,6 +13907,7 @@ public void Indexer_GetterExpressionBodyToExpressionBody() edits.VerifyEdits( "Update [int this[int a] { get => 1; }]@10 -> [int this[int a] => 1;]@10", + "Insert [=> 1]@26", "Delete [{ get => 1; }]@26", "Delete [get => 1;]@28"); @@ -13911,7 +13927,8 @@ public void Indexer_ExpressionBodyToGetterExpressionBody() edits.VerifyEdits( "Update [int this[int a] => 1;]@10 -> [int this[int a] { get => 1; }]@10", "Insert [{ get => 1; }]@26", - "Insert [get => 1;]@28"); + "Insert [get => 1;]@28", + "Delete [=> 1]@26"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -13995,6 +14012,7 @@ public void Indexer_GetterAndSetterBlockBodiesToExpressionBody() edits.VerifyEdits( "Update [int this[int a] { get { return 1; } set { Console.WriteLine(0); } }]@10 -> [int this[int a] => 1;]@10", + "Insert [=> 1]@26", "Delete [{ get { return 1; } set { Console.WriteLine(0); } }]@26", "Delete [get { return 1; }]@28", "Delete [set { Console.WriteLine(0); }]@46"); @@ -14015,7 +14033,8 @@ public void Indexer_ExpressionBodyToGetterAndSetterBlockBodies() "Update [int this[int a] => 1;]@10 -> [int this[int a] { get { return 1; } set { Console.WriteLine(0); } }]@10", "Insert [{ get { return 1; } set { Console.WriteLine(0); } }]@26", "Insert [get { return 1; }]@28", - "Insert [set { Console.WriteLine(0); }]@46"); + "Insert [set { Console.WriteLine(0); }]@46", + "Delete [=> 1]@26"); edits.VerifySemantics(ActiveStatementsDescription.Empty, new[] { @@ -14297,6 +14316,7 @@ public void Indexer_ReadOnlyRef_Parameter_InsertWhole() edits.VerifyEdits( "Insert [int this[in int i] => throw null;]@13", "Insert [[in int i]]@21", + "Insert [=> throw null]@32", "Insert [in int i]@22"); edits.VerifyRudeDiagnostics(); @@ -14328,6 +14348,7 @@ public void Indexer_ReadOnlyRef_ReturnType_Insert() edits.VerifyEdits( "Insert [ref readonly int this[int i] => throw null;]@13", "Insert [[int i]]@34", + "Insert [=> throw null]@42", "Insert [int i]@35"); edits.VerifyRudeDiagnostics(); diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index b48b918cb63bc..34e99eab90b15 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -76,6 +76,7 @@ internal enum Label IndexerDeclaration, // tied to parent EventDeclaration, // tied to parent EnumMemberDeclaration, // tied to parent + ArrowExpressionClause, // tied to parent AccessorList, // tied to parent AccessorDeclaration, // tied to parent @@ -177,6 +178,7 @@ private static int TiedToAncestor(Label label) case Label.ConstructorDeclaration: case Label.DestructorDeclaration: case Label.PropertyDeclaration: + case Label.ArrowExpressionClause: case Label.IndexerDeclaration: case Label.EventDeclaration: case Label.EnumMemberDeclaration: @@ -605,6 +607,12 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, SyntaxNode? node, out bo case SyntaxKind.IndexerDeclaration: return Label.IndexerDeclaration; + case SyntaxKind.ArrowExpressionClause: + if (node.IsParentKind(SyntaxKind.PropertyDeclaration, SyntaxKind.IndexerDeclaration)) + return Label.ArrowExpressionClause; + + break; + case SyntaxKind.EventDeclaration: return Label.EventDeclaration; @@ -1413,6 +1421,9 @@ private static double CombineOptional( case SyntaxKind.IndexerDeclaration: return null; + case SyntaxKind.ArrowExpressionClause: + return null; + case SyntaxKind.EventDeclaration: return ((EventDeclarationSyntax)node).Identifier;