/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Features/CSharpTest/EditAndContinue/StatementMatchingTests.cs
2 554 строки
91 KB
Cyrus Najmabadi
Use implicit object creation expressions
13 сен 2025, 01:48
13 сен 2025, 01:48
4c6a36a
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Generic; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests; [UseExportProvider] public sealed class StatementMatchingTests : EditingTestBase { #region Known Matches [Fact] public void KnownMatches() { var src1 = """ Console.WriteLine(1)/*1*/; Console.WriteLine(1)/*2*/; """; var src2 = """ Console.WriteLine(1)/*3*/; Console.WriteLine(1)/*4*/; """; var m1 = MakeMethodBody(src1); var m2 = MakeMethodBody(src2); var knownMatches = new KeyValuePair<SyntaxNode, SyntaxNode>[] { new(((BlockSyntax)m1.RootNodes.First()).Statements[1], ((BlockSyntax)m2.RootNodes.First()).Statements[0]) }; // pre-matched: var match = m1.ComputeSingleRootMatch(m2, knownMatches); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "Console.WriteLine(1)/*1*/;", "Console.WriteLine(1)/*4*/;" }, { "Console.WriteLine(1)/*2*/;", "Console.WriteLine(1)/*3*/;" } }; expected.AssertEqual(actual); // not pre-matched: match = m1.ComputeSingleRootMatch(m2, knownMatches: null); actual = ToMatchingPairs(match); expected = new MatchingPairs { { "Console.WriteLine(1)/*1*/;", "Console.WriteLine(1)/*3*/;" }, { "Console.WriteLine(1)/*2*/;", "Console.WriteLine(1)/*4*/;" } }; expected.AssertEqual(actual); } [Fact] public void KnownMatches_Root() { var src1 = """ Console.WriteLine(1); """; var src2 = """ Console.WriteLine(2); """; var m1 = MakeMethodBody(src1); var m2 = MakeMethodBody(src2); var knownMatches = new[] { new KeyValuePair<SyntaxNode, SyntaxNode>(m1.RootNodes.First(), m2.RootNodes.First()) }; var match = m1.ComputeSingleRootMatch(m2, knownMatches); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "Console.WriteLine(1);", "Console.WriteLine(2);" } }; expected.AssertEqual(actual); } #endregion #region Statements [Fact] public void MiscStatements() { var src1 = """ int x = 1; Console.WriteLine(1); x++/*1A*/; Console.WriteLine(2); while (true) { x++/*2A*/; } Console.WriteLine(1); """; var src2 = """ int x = 1; x++/*1B*/; for (int i = 0; i < 10; i++) {} y++; if (x > 1) { while (true) { x++/*2B*/; } Console.WriteLine(1); } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "int x = 1;", "int x = 1;" }, { "int x = 1", "int x = 1" }, { "x = 1", "x = 1" }, { "Console.WriteLine(1);", "Console.WriteLine(1);" }, { "x++/*1A*/;", "x++/*1B*/;" }, { "Console.WriteLine(2);", "y++;" }, { "while (true) { x++/*2A*/; }", "while (true) { x++/*2B*/; }" }, { "{ x++/*2A*/; }", "{ x++/*2B*/; }" }, { "x++/*2A*/;", "x++/*2B*/;" } }; expected.AssertEqual(actual); } [Fact] public void ThrowException_UpdateInsert() { var src1 = """ return a > 3 ? a : throw new Exception(); return c > 7 ? c : 7; """; var src2 = """ return a > 3 ? a : throw new ArgumentException(); return c > 7 ? c : throw new IndexOutOfRangeException(); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "return a > 3 ? a : throw new Exception();", "return a > 3 ? a : throw new ArgumentException();" }, { "return c > 7 ? c : 7;", "return c > 7 ? c : throw new IndexOutOfRangeException();" } }; expected.AssertEqual(actual); } [Fact] public void ThrowException_UpdateDelete() { var src1 = """ return a > 3 ? a : throw new Exception(); return b > 5 ? b : throw new OperationCanceledException(); """; var src2 = """ return a > 3 ? a : throw new ArgumentException(); return b > 5 ? b : 5; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "return a > 3 ? a : throw new Exception();", "return a > 3 ? a : throw new ArgumentException();" }, { "return b > 5 ? b : throw new OperationCanceledException();", "return b > 5 ? b : 5;" } }; expected.AssertEqual(actual); } [Fact] public void Tuple() { var src1 = """ return (1, 2); return (d, 6); return (10, e, 22); return (2, () => { int a = 6; return 1; }); """; var src2 = """ return (1, 2, 3); return (d, 5); return (10, e); return (2, () => { int a = 6; return 5; }); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "return (1, 2);", "return (1, 2, 3);" }, { "return (d, 6);", "return (d, 5);" }, { "return (10, e, 22);", "return (10, e);" }, { "return (2, () => { int a = 6; return 1; });", "return (2, () => { int a = 6; return 5; });" }, { "() => { int a = 6; return 1; }", "() => { int a = 6; return 5; }" }, { "()", "()" }, { "{ int a = 6; return 1; }", "{ int a = 6; return 5; }" }, { "int a = 6;", "int a = 6;" }, { "int a = 6", "int a = 6" }, { "a = 6", "a = 6" }, { "return 1;", "return 5;" } }; expected.AssertEqual(actual); } #endregion #region Local Variables [Fact] public void Locals_Rename() { var src1 = """ int x = 1; """; var src2 = """ int y = 1; """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "int x = 1;", "int y = 1;" }, { "int x = 1", "int y = 1" }, { "x = 1", "y = 1" } }; expected.AssertEqual(actual); } [Fact] public void Locals_TypeChange() { var src1 = """ int x = 1; """; var src2 = """ byte x = 1; """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "int x = 1;", "byte x = 1;" }, { "int x = 1", "byte x = 1" }, { "x = 1", "x = 1" } }; expected.AssertEqual(actual); } [Fact] public void BlocksWithLocals1() { var src1 = """ { int a = 1; } { int b = 2; } """; var src2 = """ { int a = 3; int b = 4; } { int b = 5; } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "{ int a = 1; }", "{ int a = 3; int b = 4; }" }, { "int a = 1;", "int a = 3;" }, { "int a = 1", "int a = 3" }, { "a = 1", "a = 3" }, { "{ int b = 2; }", "{ int b = 5; }" }, { "int b = 2;", "int b = 5;" }, { "int b = 2", "int b = 5" }, { "b = 2", "b = 5" } }; expected.AssertEqual(actual); } [Fact] public void IfBlocksWithLocals1() { var src1 = """ if (X) { int a = 1; } if (Y) { int b = 2; } """; var src2 = """ if (Y) { int a = 3; int b = 4; } if (X) { int b = 5; } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "if (X) { int a = 1; }", "if (Y) { int a = 3; int b = 4; }" }, { "{ int a = 1; }", "{ int a = 3; int b = 4; }" }, { "int a = 1;", "int a = 3;" }, { "int a = 1", "int a = 3" }, { "a = 1", "a = 3" }, { "if (Y) { int b = 2; }", "if (X) { int b = 5; }" }, { "{ int b = 2; }", "{ int b = 5; }" }, { "int b = 2;", "int b = 5;" }, { "int b = 2", "int b = 5" }, { "b = 2", "b = 5" } }; expected.AssertEqual(actual); } [Fact] public void BlocksWithLocals2() { var src1 = """ { int a = 1; } { { int b = 2; } } """; var src2 = """ { int b = 1; } { { int a = 2; } } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "{ int a = 1; }", "{ int a = 2; }" }, { "int a = 1;", "int a = 2;" }, { "int a = 1", "int a = 2" }, { "a = 1", "a = 2" }, { "{ { int b = 2; } }", "{ { int a = 2; } }" }, { "{ int b = 2; }", "{ int b = 1; }" }, { "int b = 2;", "int b = 1;" }, { "int b = 2", "int b = 1" }, { "b = 2", "b = 1" } }; expected.AssertEqual(actual); } [Fact] public void BlocksWithLocals3() { var src1 = """ { int a = 1, b = 2, c = 3; Console.WriteLine(a + b + c); } { int c = 4, b = 5, a = 6; Console.WriteLine(a + b + c); } { int a = 7, b = 8; Console.WriteLine(a + b); } """; var src2 = """ { int a = 9, b = 10; Console.WriteLine(a + b); } { int c = 11, b = 12, a = 13; Console.WriteLine(a + b + c); } { int a = 14, b = 15, c = 16; Console.WriteLine(a + b + c); } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "{ int a = 1, b = 2, c = 3; Console.WriteLine(a + b + c); }", "{ int a = 14, b = 15, c = 16; Console.WriteLine(a + b + c); }" }, { "int a = 1, b = 2, c = 3;", "int a = 14, b = 15, c = 16;" }, { "int a = 1, b = 2, c = 3", "int a = 14, b = 15, c = 16" }, { "a = 1", "a = 14" }, { "b = 2", "b = 15" }, { "c = 3", "c = 16" }, { "Console.WriteLine(a + b + c);", "Console.WriteLine(a + b + c);" }, { "{ int c = 4, b = 5, a = 6; Console.WriteLine(a + b + c); }", "{ int c = 11, b = 12, a = 13; Console.WriteLine(a + b + c); }" }, { "int c = 4, b = 5, a = 6;", "int c = 11, b = 12, a = 13;" }, { "int c = 4, b = 5, a = 6", "int c = 11, b = 12, a = 13" }, { "c = 4", "c = 11" }, { "b = 5", "b = 12" }, { "a = 6", "a = 13" }, { "Console.WriteLine(a + b + c);", "Console.WriteLine(a + b + c);" }, { "{ int a = 7, b = 8; Console.WriteLine(a + b); }", "{ int a = 9, b = 10; Console.WriteLine(a + b); }" }, { "int a = 7, b = 8;", "int a = 9, b = 10;" }, { "int a = 7, b = 8", "int a = 9, b = 10" }, { "a = 7", "a = 9" }, { "b = 8", "b = 10" }, { "Console.WriteLine(a + b);", "Console.WriteLine(a + b);" } }; expected.AssertEqual(actual); } [Fact] public void VariableDesignations() { var src1 = """ M(out int z); N(out var a); """; var src2 = """ M(out var z); N(out var b); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "M(out int z);", "M(out var z);" }, { "z", "z" }, { "N(out var a);", "N(out var b);" }, { "a", "b" } }; expected.AssertEqual(actual); } [Fact] public void ParenthesizedVariable_Update() { var src1 = """ var (x1, (x2, x3, _)) = (1, (2, true, 3)); var (a1, a2) = (1, () => { return 7; }); """; var src2 = """ var (x1, (x3, x4)) = (1, (2, true)); var (a1, a3) = (1, () => { return 8; }); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var (x1, (x2, x3, _)) = (1, (2, true, 3));", "var (x1, (x3, x4)) = (1, (2, true));" }, { "x1", "x1" }, { "x2", "x4" }, { "x3", "x3" }, { "var (a1, a2) = (1, () => { return 7; });", "var (a1, a3) = (1, () => { return 8; });" }, { "a1", "a1" }, { "a2", "a3" }, { "() => { return 7; }", "() => { return 8; }" }, { "()", "()" }, { "{ return 7; }", "{ return 8; }" }, { "return 7;", "return 8;" } }; expected.AssertEqual(actual); } [Fact] public void ParenthesizedVariable_Insert() { var src1 = @"var (z1, z2) = (1, 2);"; var src2 = @"var (z1, z2, z3) = (1, 2, 5);"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var (z1, z2) = (1, 2);", "var (z1, z2, z3) = (1, 2, 5);" }, { "z1", "z1" }, { "z2", "z2" } }; expected.AssertEqual(actual); } [Fact] public void ParenthesizedVariable_Delete() { var src1 = @"var (y1, y2, y3) = (1, 2, 7);"; var src2 = @"var (y1, y2) = (1, 4);"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var (y1, y2, y3) = (1, 2, 7);", "var (y1, y2) = (1, 4);" }, { "y1", "y1" }, { "y2", "y2" } }; expected.AssertEqual(actual); } [Fact] public void RefVariable() { var src1 = """ ref int a = ref G(new int[] { 1, 2 }); ref int G(int[] p) { return ref p[1]; } """; var src2 = """ ref int32 a = ref G1(new int[] { 1, 2 }); ref int G1(int[] p) { return ref p[2]; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "ref int a = ref G(new int[] { 1, 2 });", "ref int32 a = ref G1(new int[] { 1, 2 });" }, { "ref int a = ref G(new int[] { 1, 2 })", "ref int32 a = ref G1(new int[] { 1, 2 })" }, { "a = ref G(new int[] { 1, 2 })", "a = ref G1(new int[] { 1, 2 })" }, { "ref int G(int[] p) { return ref p[1]; }", "ref int G1(int[] p) { return ref p[2]; }" }, { "(int[] p)", "(int[] p)" }, { "int[] p", "int[] p" }, { "{ return ref p[1]; }", "{ return ref p[2]; }" }, { "return ref p[1];", "return ref p[2];" } }; expected.AssertEqual(actual); } #endregion #region Lambdas [Fact] public void Lambdas1() { var src1 = "Action x = a => a;"; var src2 = "Action x = (a) => a;"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "Action x = a => a;", "Action x = (a) => a;" }, { "Action x = a => a", "Action x = (a) => a" }, { "x = a => a", "x = (a) => a" }, { "a => a", "(a) => a" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas2a() { var src1 = """ F(x => x + 1, 1, y => y + 1, delegate(int x) { return x; }, async u => u); """; var src2 = """ F(y => y + 1, G(), x => x + 1, (int x) => x, u => u, async (u, v) => u + v); """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(x => x + 1, 1, y => y + 1, delegate(int x) { return x; }, async u => u);", "F(y => y + 1, G(), x => x + 1, (int x) => x, u => u, async (u, v) => u + v);" }, { "x => x + 1", "x => x + 1" }, { "x", "x" }, { "y => y + 1", "y => y + 1" }, { "y", "y" }, { "delegate(int x) { return x; }", "(int x) => x" }, { "(int x)", "(int x)" }, { "int x", "int x" }, { "async u => u", "async (u, v) => u + v" } }; expected.AssertEqual(actual); } [Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/830419")] public void Lambdas2b() { var src1 = """ F(delegate { return x; }); """; var src2 = """ F((a) => x, () => x); """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(delegate { return x; });", "F((a) => x, () => x);" }, { "delegate { return x; }", "() => x" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas3() { var src1 = """ a += async u => u; """; var src2 = """ a += u => u; """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "a += async u => u;", "a += u => u;" }, { "async u => u", "u => u" }, { "u", "u" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas4() { var src1 = """ foreach (var a in z) { var e = from q in a.Where(l => l > 10) select q + 1; } """; var src2 = """ foreach (var a in z) { var e = from q in a.Where(l => l < 0) select q + 1; } """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "foreach (var a in z) { var e = from q in a.Where(l => l > 10) select q + 1; }", "foreach (var a in z) { var e = from q in a.Where(l => l < 0) select q + 1; }" }, { "{ var e = from q in a.Where(l => l > 10) select q + 1; }", "{ var e = from q in a.Where(l => l < 0) select q + 1; }" }, { "var e = from q in a.Where(l => l > 10) select q + 1;", "var e = from q in a.Where(l => l < 0) select q + 1;" }, { "var e = from q in a.Where(l => l > 10) select q + 1", "var e = from q in a.Where(l => l < 0) select q + 1" }, { "e = from q in a.Where(l => l > 10) select q + 1", "e = from q in a.Where(l => l < 0) select q + 1" }, { "from q in a.Where(l => l > 10)", "from q in a.Where(l => l < 0)" }, { "l => l > 10", "l => l < 0" }, { "l", "l" }, { "select q + 1", "select q + 1" }, // select clause { "select q + 1", "select q + 1" } // query body }; expected.AssertEqual(actual); } [Fact] public void Lambdas5() { var src1 = """ F(a => b => c => d); """; var src2 = """ F(a => b => c => d); """; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "F(a => b => c => d);", "F(a => b => c => d);" }, { "a => b => c => d", "a => b => c => d" }, { "a", "a" }, { "b => c => d", "b => c => d" }, { "b", "b" }, { "c => d", "c => d" }, { "c", "c" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas6() { var src1 = """ F(a => b => c => d); """; var src2 = """ F(a => G(b => H(c => I(d)))); """; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "F(a => b => c => d);", "F(a => G(b => H(c => I(d))));" }, { "a => b => c => d", "a => G(b => H(c => I(d)))" }, { "a", "a" }, { "b => c => d", "b => H(c => I(d))" }, { "b", "b" }, { "c => d", "c => I(d)" }, { "c", "c" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas7() { var src1 = """ F(a => { F(c => /*1*/d); F((u, v) => { F((w) => c => /*2*/d); F(p => p); }); }); """; var src2 = """ F(a => { F(c => /*1*/d + 1); F((u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); }); }); """; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "F(a => { F(c => /*1*/d); F((u, v) => { F((w) => c => /*2*/d); F(p => p); }); });", "F(a => { F(c => /*1*/d + 1); F((u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); }); });" }, { "a => { F(c => /*1*/d); F((u, v) => { F((w) => c => /*2*/d); F(p => p); }); }", "a => { F(c => /*1*/d + 1); F((u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); }); }" }, { "a", "a" }, { "{ F(c => /*1*/d); F((u, v) => { F((w) => c => /*2*/d); F(p => p); }); }", "{ F(c => /*1*/d + 1); F((u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); }); }" }, { "F(c => /*1*/d);", "F(c => /*1*/d + 1);" }, { "c => /*1*/d", "c => /*1*/d + 1" }, { "c", "c" }, { "F((u, v) => { F((w) => c => /*2*/d); F(p => p); });", "F((u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); });" }, { "(u, v) => { F((w) => c => /*2*/d); F(p => p); }", "(u, v) => { F((w) => c => /*2*/d + 1); F(p => p*2); }" }, { "(u, v)", "(u, v)" }, { "u", "u" }, { "v", "v" }, { "{ F((w) => c => /*2*/d); F(p => p); }", "{ F((w) => c => /*2*/d + 1); F(p => p*2); }" }, { "F((w) => c => /*2*/d);", "F((w) => c => /*2*/d + 1);" }, { "(w) => c => /*2*/d", "(w) => c => /*2*/d + 1" }, { "(w)", "(w)" }, { "w", "w" }, { "c => /*2*/d", "c => /*2*/d + 1" }, { "c", "c" }, { "F(p => p);", "F(p => p*2);" }, { "p => p", "p => p*2" }, { "p", "p" } }; expected.AssertEqual(actual); } [Fact] public void LambdasInArrayType() { var src1 = "var x = new int[F(a => 1)];"; var src2 = "var x = new int[F(a => 2)];"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var x = new int[F(a => 1)];", "var x = new int[F(a => 2)];" }, { "var x = new int[F(a => 1)]", "var x = new int[F(a => 2)]" }, { "x = new int[F(a => 1)]", "x = new int[F(a => 2)]" }, { "a => 1", "a => 2" }, { "a", "a" } }; expected.AssertEqual(actual); } [Fact] public void LambdasInArrayInitializer() { var src1 = "var x = new int[] { F(a => 1) };"; var src2 = "var x = new int[] { F(a => 2) };"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var x = new int[] { F(a => 1) };", "var x = new int[] { F(a => 2) };" }, { "var x = new int[] { F(a => 1) }", "var x = new int[] { F(a => 2) }" }, { "x = new int[] { F(a => 1) }", "x = new int[] { F(a => 2) }" }, { "a => 1", "a => 2" }, { "a", "a" } }; expected.AssertEqual(actual); } [Fact] public void LambdasInStackalloc() { var src1 = "var x = stackalloc int[F(a => 1)];"; var src2 = "var x = stackalloc int[F(a => 2)];"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var x = stackalloc int[F(a => 1)];", "var x = stackalloc int[F(a => 2)];" }, { "var x = stackalloc int[F(a => 1)]", "var x = stackalloc int[F(a => 2)]" }, { "x = stackalloc int[F(a => 1)]", "x = stackalloc int[F(a => 2)]" }, { "a => 1", "a => 2" }, { "a", "a" } }; expected.AssertEqual(actual); } [Fact] public void LambdasInStackalloc_Initializer() { var src1 = "var x = stackalloc[] { F(a => 1) };"; var src2 = "var x = stackalloc[] { F(a => 2) };"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var x = stackalloc[] { F(a => 1) };", "var x = stackalloc[] { F(a => 2) };" }, { "var x = stackalloc[] { F(a => 1) }", "var x = stackalloc[] { F(a => 2) }" }, { "x = stackalloc[] { F(a => 1) }", "x = stackalloc[] { F(a => 2) }" }, { "a => 1", "a => 2" }, { "a", "a" } }; expected.AssertEqual(actual); } [Fact] public void Lambdas_ParameterToDiscard() { var src1 = "var x = F((a, b) => 1);"; var src2 = "var x = F((_, _) => 2);"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var x = F((a, b) => 1);", "var x = F((_, _) => 2);" }, { "var x = F((a, b) => 1)", "var x = F((_, _) => 2)" }, { "x = F((a, b) => 1)", "x = F((_, _) => 2)" }, { "(a, b) => 1", "(_, _) => 2" }, { "(a, b)", "(_, _)" }, { "a", "_" }, { "b", "_" } }; expected.AssertEqual(actual); } #endregion #region Local Functions [Fact] public void LocalFunctionDefinitions() { var src1 = """ (int a, string c) F1(int i) { return null; } (int a, int b) F2(int i) { return null; } (int a, int b, int c) F3(int i) { return null; } """; var src2 = """ (int a, int b) F1(int i) { return null; } (int a, int b, string c) F2(int i) { return null; } (int a, int b) F3(int i) { return null; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "(int a, string c) F1(int i) { return null; }", "(int a, int b) F1(int i) { return null; }" }, { "(int i)", "(int i)" }, { "int i", "int i" }, { "{ return null; }", "{ return null; }" }, { "return null;", "return null;" }, { "(int a, int b) F2(int i) { return null; }", "(int a, int b, string c) F2(int i) { return null; }" }, { "(int i)", "(int i)" }, { "int i", "int i" }, { "{ return null; }", "{ return null; }" }, { "return null;", "return null;" }, { "(int a, int b, int c) F3(int i) { return null; }", "(int a, int b) F3(int i) { return null; }" }, { "(int i)", "(int i)" }, { "int i", "int i" }, { "{ return null; }", "{ return null; }" }, { "return null;", "return null;" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions1() { var src1 = "object x(object a) => a; F(x);"; var src2 = "F(a => a);"; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "object x(object a) => a;", "a => a" }, { "F(x);", "F(a => a);" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions2a() { var src1 = """ F(x => x + 1, 1, y => y + 1, delegate(int x) { return x; }, async u => u); """; var src2 = """ int localF1(int y) => y + 1; int localF2(int x) => x + 1; int localF3(int x) => x; int localF4(int u) => u; async int localF5(int u, int v) => u + v; F(localF1, localF2, G(), localF2, localF3, localF4, localF5); """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(x => x + 1, 1, y => y + 1, delegate(int x) { return x; }, async u => u);", "F(localF1, localF2, G(), localF2, localF3, localF4, localF5);" }, { "x => x + 1", "int localF2(int x) => x + 1;" }, { "y => y + 1", "int localF1(int y) => y + 1;" }, { "delegate(int x) { return x; }", "int localF3(int x) => x;" }, { "(int x)", "(int x)" }, { "int x", "int x" }, { "async u => u", "int localF4(int u) => u;" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions2b() { var src1 = """ F(delegate { return x; }); """; var src2 = """ int localF() => x; F(localF); """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(delegate { return x; });", "F(localF);" }, { "delegate { return x; }", "int localF() => x;" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions3() { var src1 = """ a += async u => u; """; var src2 = """ object localF(object u) => u; a += localF; """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "a += async u => u;", "a += localF;" }, { "async u => u", "object localF(object u) => u;" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions4() { var src1 = @"int a() { int b() { int c() { int d() { return 0; } } return c(); } return b(); }"; var src2 = @"int a() { int b() { int c() { int d() { return 0; } } return c(); } return b(); }"; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "int a() { int b() { int c() { int d() { return 0; } } return c(); } return b(); }", "int a() { int b() { int c() { int d() { return 0; } } return c(); } return b(); }" }, { "()", "()" }, { "{ int b() { int c() { int d() { return 0; } } return c(); } return b(); }", "{ int b() { int c() { int d() { return 0; } } return c(); } return b(); }" }, { "int b() { int c() { int d() { return 0; } } return c(); }", "int b() { int c() { int d() { return 0; } } return c(); }" }, { "()", "()" }, { "{ int c() { int d() { return 0; } } return c(); }", "{ int c() { int d() { return 0; } } return c(); }" }, { "int c() { int d() { return 0; } }", "int c() { int d() { return 0; } }" }, { "()", "()" }, { "{ int d() { return 0; } }", "{ int d() { return 0; } }" }, { "int d() { return 0; }", "int d() { return 0; }" }, { "()", "()" }, { "{ return 0; }", "{ return 0; }" }, { "return 0;", "return 0;" }, { "return c();", "return c();" }, { "return b();", "return b();" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions5() { var src1 = """ void G6(int a) { int G5(int c) => /*1*/d; F(G5); void G4() { void G1(int x) => x; int G3(int w) { int G2(int c) => /*2*/d; return G2(w); } F(G3); F(G1); }; F(G4); } """; var src2 = """ void G6(int a) { int G5(int c) => /*1*/d + 1;F(G5); void G4() { int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); } F(G3); F(G1); int G6(int p) => p *2; F(G6); } F(G4); } """; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "void G6(int a) { int G5(int c) => /*1*/d; F(G5); void G4() { void G1(int x) => x; int G3(int w) { int G2(int c) => /*2*/d; return G2(w); } F(G3); F(G1); }; F(G4); }", "void G6(int a) { int G5(int c) => /*1*/d + 1;F(G5); void G4() { int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); } F(G3); F(G1); int G6(int p) => p *2; F(G6); } F(G4); }" }, { "(int a)", "(int a)" }, { "int a", "int a" }, { "{ int G5(int c) => /*1*/d; F(G5); void G4() { void G1(int x) => x; int G3(int w) { int G2(int c) => /*2*/d; return G2(w); } F(G3); F(G1); }; F(G4); }", "{ int G5(int c) => /*1*/d + 1;F(G5); void G4() { int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); } F(G3); F(G1); int G6(int p) => p *2; F(G6); } F(G4); }" }, { "int G5(int c) => /*1*/d;", "int G5(int c) => /*1*/d + 1;" }, { "(int c)", "(int c)" }, { "int c", "int c" }, { "F(G5);", "F(G5);" }, { "void G4() { void G1(int x) => x; int G3(int w) { int G2(int c) => /*2*/d; return G2(w); } F(G3); F(G1); }", "void G4() { int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); } F(G3); F(G1); int G6(int p) => p *2; F(G6); }" }, { "()", "()" }, { "{ void G1(int x) => x; int G3(int w) { int G2(int c) => /*2*/d; return G2(w); } F(G3); F(G1); }", "{ int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); } F(G3); F(G1); int G6(int p) => p *2; F(G6); }" }, { "void G1(int x) => x;", "int G6(int p) => p *2;" }, { "(int x)", "(int p)" }, { "int x", "int p" }, { "int G3(int w) { int G2(int c) => /*2*/d; return G2(w); }", "int G3(int w) { int G2(int c) => /*2*/d + 1; return G2(w); }" }, { "(int w)", "(int w)" }, { "int w", "int w" }, { "{ int G2(int c) => /*2*/d; return G2(w); }", "{ int G2(int c) => /*2*/d + 1; return G2(w); }" }, { "int G2(int c) => /*2*/d;", "int G2(int c) => /*2*/d + 1;" }, { "(int c)", "(int c)" }, { "int c", "int c" }, { "return G2(w);", "return G2(w);" }, { "F(G3);", "F(G3);" }, { "F(G1);", "F(G1);" }, { "F(G4);", "F(G4);" } }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions6() { var src1 = @"int f() { return local(); int local() { return 1; }}"; var src2 = @"int f() { return local(); int local() => 2; }"; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "int f() { return local(); int local() { return 1; }}", "int f() { return local(); int local() => 2; }" }, { "()", "()" }, { "{ return local(); int local() { return 1; }}", "{ return local(); int local() => 2; }" }, { "return local();", "return local();" }, { "int local() { return 1; }", "int local() => 2;" }, { "()", "()" }, }; expected.AssertEqual(actual); } [Fact] public void LocalFunctions6Reverse() { var src1 = @"int f() { return local(); int local() => 2; }"; var src2 = @"int f() { return local(); int local() { return 1; }}"; var matches = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(matches); var expected = new MatchingPairs { { "int f() { return local(); int local() => 2; }", "int f() { return local(); int local() { return 1; }}" }, { "()", "()" }, { "{ return local(); int local() => 2; }", "{ return local(); int local() { return 1; }}" }, { "return local();", "return local();" }, { "int local() => 2;", "int local() { return 1; }" }, { "()", "()" }, }; expected.AssertEqual(actual); } #endregion #region LINQ [Fact] public void Queries1() { var src1 = """ var q = from c in cars from ud in users_details from bd in bids select 1; """; var src2 = """ var q = from c in cars from bd in bids from ud in users_details select 2; """; var match = GetMethodMatch(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var q = from c in cars from ud in users_details from bd in bids select 1;", "var q = from c in cars from bd in bids from ud in users_details select 2;" }, { "var q = from c in cars from ud in users_details from bd in bids select 1", "var q = from c in cars from bd in bids from ud in users_details select 2" }, { "q = from c in cars from ud in users_details from bd in bids select 1", "q = from c in cars from bd in bids from ud in users_details select 2" }, { "from c in cars", "from c in cars" }, { "from ud in users_details from bd in bids select 1", "from bd in bids from ud in users_details select 2" }, { "from ud in users_details", "from ud in users_details" }, { "from bd in bids", "from bd in bids" }, { "select 1", "select 2" } }; expected.AssertEqual(actual); } [Fact] public void Queries2() { var src1 = """ var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id select ai let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault() select bid; """; var src2 = """ var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1 let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault() select bid; """; var match = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id select ai let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault() select bid;", "var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1 let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault() select bid;" }, { "var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id select ai let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault() select bid", "var q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1 let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault() select bid" }, { "q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id select ai let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault() select bid", "q = from c in cars from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1 let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault() select bid" }, { "from c in cars", "from c in cars" }, { "from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id select ai let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault() select bid", "from ud in users_details from bd in bids orderby c.listingOption descending where a.userID == ud.userid let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1 let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault() select bid" }, { "from ud in users_details", "from ud in users_details" }, { "from bd in bids", "from bd in bids" }, { "orderby c.listingOption descending", "orderby c.listingOption descending" }, { "c.listingOption descending", "c.listingOption descending" }, { "where a.userID == ud.userid", "where a.userID == ud.userid" }, { "let images = from ai in auction_images where ai.belongs_to == c.id select ai", "let images = from ai in auction_images where ai.belongs_to == c.id2 select ai + 1" }, { "from ai in auction_images", "from ai in auction_images" }, { "where ai.belongs_to == c.id select ai", "where ai.belongs_to == c.id2 select ai + 1" }, { "where ai.belongs_to == c.id", "where ai.belongs_to == c.id2" }, { "select ai", "select ai + 1" }, { "let bid = (from b in bids orderby b.id descending where b.carID == c.id select b.bidamount).FirstOrDefault()", "let bid = (from b in bids orderby b.id ascending where b.carID == c.id2 select b.bidamount).FirstOrDefault()" }, { "from b in bids", "from b in bids" }, { "orderby b.id descending where b.carID == c.id select b.bidamount", "orderby b.id ascending where b.carID == c.id2 select b.bidamount" }, { "orderby b.id descending", "orderby b.id ascending" }, { "b.id descending", "b.id ascending" }, { "where b.carID == c.id", "where b.carID == c.id2" }, { "select b.bidamount", "select b.bidamount" }, { "select bid", "select bid" } }; expected.AssertEqual(actual); } [Fact] public void Queries3() { var src1 = """ var q = from a in await seq1 join c in await seq2 on F(u => u) equals G(s => s) into g1 join l in await seq3 on F(v => v) equals G(t => t) into g2 select a; """; var src2 = """ var q = from a in await seq1 join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1 join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2 select a + 1; """; var match = GetMethodMatches(src1, src2, MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var q = from a in await seq1 join c in await seq2 on F(u => u) equals G(s => s) into g1 join l in await seq3 on F(v => v) equals G(t => t) into g2 select a;", "var q = from a in await seq1 join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1 join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2 select a + 1;" }, { "var q = from a in await seq1 join c in await seq2 on F(u => u) equals G(s => s) into g1 join l in await seq3 on F(v => v) equals G(t => t) into g2 select a", "var q = from a in await seq1 join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1 join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2 select a + 1" }, { "q = from a in await seq1 join c in await seq2 on F(u => u) equals G(s => s) into g1 join l in await seq3 on F(v => v) equals G(t => t) into g2 select a", "q = from a in await seq1 join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1 join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2 select a + 1" }, { "from a in await seq1", "from a in await seq1" }, { "await seq1", "await seq1" }, { "join c in await seq2 on F(u => u) equals G(s => s) into g1 join l in await seq3 on F(v => v) equals G(t => t) into g2 select a", "join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1 join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2 select a + 1" }, { "join c in await seq2 on F(u => u) equals G(s => s) into g1", "join c in await seq2 on F(u => u + 1) equals G(s => s + 3) into g1" }, { "await seq2", "await seq2" }, { "u => u", "u => u + 1" }, { "u", "u" }, { "s => s", "s => s + 3" }, { "s", "s" }, { "into g1", "into g1" }, { "join l in await seq3 on F(v => v) equals G(t => t) into g2", "join c in await seq3 on F(vv => vv + 2) equals G(tt => tt + 4) into g2" }, { "await seq3", "await seq3" }, { "v => v", "vv => vv + 2" }, { "v", "vv" }, { "t => t", "tt => tt + 4" }, { "t", "tt" }, { "into g2", "into g2" }, { "select a", "select a + 1" } }; expected.AssertEqual(actual); } [Fact] public void Queries4() { var src1 = "F(from a in await b from x in y select c);"; var src2 = "F(from a in await c from x in y select c);"; var match = GetMethodMatches(src1, src2, MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(from a in await b from x in y select c);", "F(from a in await c from x in y select c);" }, { "from a in await b", "from a in await c" }, { "await b", "await c" }, { "from x in y select c", "from x in y select c" }, { "from x in y", "from x in y" }, { "select c", "select c" } }; expected.AssertEqual(actual); } [Fact] public void Queries5() { var src1 = "F(from a in b group a by a.x into g select g);"; var src2 = "F(from a in b group z by z.y into h select h);"; var match = GetMethodMatches(src1, src2); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(from a in b group a by a.x into g select g);", "F(from a in b group z by z.y into h select h);" }, { "from a in b", "from a in b" }, { "group a by a.x into g select g", "group z by z.y into h select h" }, { "group a by a.x", "group z by z.y" }, { "into g select g", "into h select h" }, { "select g", "select h" }, { "select g", "select h" } }; expected.AssertEqual(actual); } #endregion #region Iterators [Fact] public void Yields() { var src1 = """ yield return 0; yield return 1; """; var src2 = """ yield break; yield return 1; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Iterator); var actual = ToMatchingPairs(match); // yield return should not match yield break var expected = new MatchingPairs() { { "yield return 1;", "yield return 1;" } }; expected.AssertEqual(actual); } [Fact] public void YieldReturn_Add() { var src1 = """ yield return /*1*/ 1; yield return /*2*/ 2; """; var src2 = """ yield return /*3*/ 3; yield return /*1*/ 1; yield return /*2*/ 2; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Iterator); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "yield return /*1*/ 1;", "yield return /*1*/ 1;" }, { "yield return /*2*/ 2;", "yield return /*2*/ 2;" } }; expected.AssertEqual(actual); } [Fact] public void YieldReturn_Swap1() { var src1 = """ A(); yield return /*1*/ 1; B(); yield return /*2*/ 2; C(); """; var src2 = """ B(); yield return /*2*/ 2; A(); yield return /*1*/ 1; C(); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Iterator); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "A();", "A();" }, { "yield return /*1*/ 1;", "yield return /*1*/ 1;" }, { "B();", "B();" }, { "yield return /*2*/ 2;", "yield return /*2*/ 2;" }, { "C();", "C();" } }; expected.AssertEqual(actual); } [Fact] public void YieldReturn_Swap2() { var src1 = """ yield return /*1*/ 1; { yield return /*2*/ 2; } foreach (var x in y) { yield return /*3*/ 3; } """; var src2 = """ yield return /*1*/ 1; yield return /*2*/ 3; foreach (var x in y) { yield return /*3*/ 2; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Iterator); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "yield return /*1*/ 1;", "yield return /*1*/ 1;" }, { "{ yield return /*2*/ 2; }", "{ yield return /*3*/ 2; }" }, { "yield return /*2*/ 2;", "yield return /*3*/ 2;" }, { "foreach (var x in y) { yield return /*3*/ 3; }", "foreach (var x in y) { yield return /*3*/ 2; }" } }; expected.AssertEqual(actual); } #endregion #region Async [Fact] public void AwaitExpressions() { var src1 = "F(await x, await y);"; var src2 = "F(await y, await x);"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F(await x, await y);", "F(await y, await x);" }, { "await x", "await x" }, { "await y", "await y" } }; expected.AssertEqual(actual); } [Fact] public void Awaits() { var src1 = """ await x; await using (expr) {} await using (D y = new D()) {} await using D y = new D(); await foreach (var z in w) {} await foreach (var (u, v) in w) {} """; var src2 = """ await foreach (var (u, v) in w) {} await foreach (var z in w) {} await using D y = new D(); await using (D y = new D()) {} await using (expr) {} await x; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "await x;", "await x;" }, { "await x", "await x" }, { "await using (expr) {}", "await using (expr) {}" }, { "{}", "{}" }, { "await using (D y = new D()) {}", "await using (D y = new D()) {}" }, { "D y = new D()", "D y = new D()" }, { "y = new D()", "y = new D()" }, { "{}", "{}" }, { "await using D y = new D();", "await using D y = new D();" }, { "D y = new D()", "D y = new D()" }, { "y = new D()", "y = new D()" }, { "await foreach (var z in w) {}", "await foreach (var z in w) {}" }, { "{}", "{}" }, { "await foreach (var (u, v) in w) {}", "await foreach (var (u, v) in w) {}" }, { "u", "u" }, { "v", "v" }, { "{}", "{}" } }; expected.AssertEqual(actual); } [Fact] public void Await_To_AwaitUsingExpression() { var src1 = """ await x; """; var src2 = """ await using (expr) {} """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs(); expected.AssertEqual(actual); } [Fact] public void Await_To_AwaitUsingDecl() { var src1 = """ await x; """; var src2 = """ await using D y = new D(); """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // empty - treated as different awaits as the await using awaits at the end of the scope var expected = new MatchingPairs(); expected.AssertEqual(actual); } [Fact] public void AwaitUsingDecl_To_AwaitUsingStatement() { var src1 = """ await using D y = new D(); """; var src2 = """ await using (D y = new D()) { } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs(); expected.AssertEqual(actual); } [Fact] public void AwaitUsingExpression_To_AwaitUsingStatementWithSingleVariable() { var src1 = """ await using (y = new D()) { } """; var src2 = """ await using (D y = new D()) { } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // Using with a single variable could match using with an expression because they both generate a single try-finally block, // but to simplify logic we do not match them currently. var expected = new MatchingPairs { { "{ }", "{ }" } }; expected.AssertEqual(actual); } [Fact] public void AwaitUsingExpression_To_AwaitUsingStatementWithMultipleVariables() { var src1 = """ await using (y = new D()) { } """; var src2 = """ await using (D y = new D(), z = new D()) { } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // Using with multiple variables should not match using with an expression because they generate different number of try-finally blocks. var expected = new MatchingPairs { { "{ }", "{ }" } }; expected.AssertEqual(actual); } [Fact] public void AwaitUsingMatchesUSing() { var src1 = "await x;foreach (T x in y) {}"; var src2 = "await x;await foreach (T x in y) {}"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs() { { "await x;", "await x;" }, { "await x", "await x" }, { "foreach (T x in y) {}", "await foreach (T x in y) {}" }, { "{}", "{}" } }; expected.AssertEqual(actual); } [Fact] public void Await_To_AwaitForeach() { var src1 = """ await x; """; var src2 = """ await foreach (var x in y) {} """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // empty - treated as different awaits as the foreach awaits in each loop iteration var expected = new MatchingPairs(); expected.AssertEqual(actual); } [Fact] public void Await_To_AwaitForeachVar() { var src1 = """ await x; """; var src2 = """ await foreach (var (x, y) in z) {} """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // empty - treated as different awaits as the foreach awaits in each loop iteration var expected = new MatchingPairs(); expected.AssertEqual(actual); } [Fact] public void AwaitForeach_To_AwaitForeachVar() { var src1 = """ await foreach (var x in y) {} """; var src2 = """ await foreach (var (u, v) in y) {} """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "await foreach (var x in y) {}", "await foreach (var (u, v) in y) {}" }, { "{}", "{}" } }; expected.AssertEqual(actual); } [Fact] public void AwaitForeachMatchesForeach() { var src1 = "await x;foreach (T x in y) {}"; var src2 = "await x;await foreach (T x in y) {}"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); // We do match await foreach to foreach, even though the latter does not represent state machine. // This is ok since the previous version of the method won't have state machine state associated with the // foreach statement and thus matching state machine state for the await foreach won't succeed even though // the syntax nodes match. var expected = new MatchingPairs() { { "await x;", "await x;" }, { "await x", "await x" }, { "foreach (T x in y) {}", "await foreach (T x in y) {}" }, { "{}", "{}" } }; expected.AssertEqual(actual); } #endregion #region Constructors [Fact] public void ConstructorWithInitializer1() { var src1 = """ (int x = 1) : base(a => a + 1) { Console.WriteLine(1); } """; var src2 = """ (int x = 1) : base(a => a + 1) { Console.WriteLine(1); } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.ConstructorWithParameters); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "(int x = 1)", "(int x = 1)" }, { "int x = 1", "int x = 1" }, { "a => a + 1", "a => a + 1" }, { "a", "a" }, { "{ Console.WriteLine(1); }", "{ Console.WriteLine(1); }" }, { "Console.WriteLine(1);", "Console.WriteLine(1);" } }; expected.AssertEqual(actual); } [Fact] public void ConstructorWithInitializer2() { var src1 = """ () : base(a => a + 1) { Console.WriteLine(1); } """; var src2 = """ () { Console.WriteLine(1); } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.ConstructorWithParameters); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "()", "()" }, { "{ Console.WriteLine(1); }", "{ Console.WriteLine(1); }" }, { "Console.WriteLine(1);", "Console.WriteLine(1);" } }; expected.AssertEqual(actual); } #endregion #region Exception Handlers [Fact] public void ExceptionHandlers() { var src1 = """ try { throw new InvalidOperationException(1); } catch (IOException e) when (filter(e)) { Console.WriteLine(2); } catch (Exception e) when (filter(e)) { Console.WriteLine(3); } """; var src2 = """ try { throw new InvalidOperationException(10); } catch (IOException e) when (filter(e)) { Console.WriteLine(20); } catch (Exception e) when (filter(e)) { Console.WriteLine(30); } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "try { throw new InvalidOperationException(1); } catch (IOException e) when (filter(e)) { Console.WriteLine(2); } catch (Exception e) when (filter(e)) { Console.WriteLine(3); }", "try { throw new InvalidOperationException(10); } catch (IOException e) when (filter(e)) { Console.WriteLine(20); } catch (Exception e) when (filter(e)) { Console.WriteLine(30); }" }, { "{ throw new InvalidOperationException(1); }", "{ throw new InvalidOperationException(10); }" }, { "throw new InvalidOperationException(1);", "throw new InvalidOperationException(10);" }, { "catch (IOException e) when (filter(e)) { Console.WriteLine(2); }", "catch (IOException e) when (filter(e)) { Console.WriteLine(20); }" }, { "(IOException e)", "(IOException e)" }, { "when (filter(e))", "when (filter(e))" }, { "{ Console.WriteLine(2); }", "{ Console.WriteLine(20); }" }, { "Console.WriteLine(2);", "Console.WriteLine(20);" }, { "catch (Exception e) when (filter(e)) { Console.WriteLine(3); }", "catch (Exception e) when (filter(e)) { Console.WriteLine(30); }" }, { "(Exception e)", "(Exception e)" }, { "when (filter(e))", "when (filter(e))" }, { "{ Console.WriteLine(3); }", "{ Console.WriteLine(30); }" }, { "Console.WriteLine(3);", "Console.WriteLine(30);" } }; expected.AssertEqual(actual); } #endregion #region Foreach [Fact] public void ForeachVariable_Update1() { var src1 = """ foreach (var (a1, a2) in e) { A1(); } foreach ((var b1, var b2) in e) { A2(); } """; var src2 = """ foreach (var (a1, a3) in e) { A1(); } foreach ((var b3, int b2) in e) { A2(); } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "foreach (var (a1, a2) in e) { A1(); }", "foreach (var (a1, a3) in e) { A1(); }" }, { "a1", "a1" }, { "a2", "a3" }, { "{ A1(); }", "{ A1(); }" }, { "A1();", "A1();" }, { "foreach ((var b1, var b2) in e) { A2(); }", "foreach ((var b3, int b2) in e) { A2(); }" }, { "b1", "b3" }, { "b2", "b2" }, { "{ A2(); }", "{ A2(); }" }, { "A2();", "A2();" }, }; expected.AssertEqual(actual); } [Fact] public void ForeachVariable_Update2() { var src1 = """ foreach (_ in e2) { } foreach (_ in e3) { A(); } """; var src2 = """ foreach (_ in e4) { A(); } foreach (var b in e2) { } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "foreach (_ in e2) { }", "foreach (var b in e2) { }" }, { "{ }", "{ }" }, { "foreach (_ in e3) { A(); }", "foreach (_ in e4) { A(); }" }, { "{ A(); }", "{ A(); }" }, { "A();", "A();" } }; expected.AssertEqual(actual); } [Fact] public void ForeachVariable_Insert() { var src1 = """ foreach (var ((a3, a4), _) in e) { } foreach ((var b4, var b5) in e) { } """; var src2 = """ foreach (var ((a3, a5, a4), _) in e) { } foreach ((var b6, var b4, var b5) in e) { } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "foreach (var ((a3, a4), _) in e) { }", "foreach (var ((a3, a5, a4), _) in e) { }" }, { "a3", "a3" }, { "a4", "a4" }, { "{ }", "{ }" }, { "foreach ((var b4, var b5) in e) { }", "foreach ((var b6, var b4, var b5) in e) { }" }, { "b4", "b4" }, { "b5", "b5" }, { "{ }", "{ }" } }; expected.AssertEqual(actual); } [Fact] public void ForeachVariable_Delete() { var src1 = """ foreach (var (a11, a12, a13) in e) { A1(); } foreach ((var b7, var b8, var b9) in e) { A2(); } """; var src2 = """ foreach (var (a12, a13) in e1) { A1(); } foreach ((var b7, var b9) in e) { A2(); } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "foreach (var (a11, a12, a13) in e) { A1(); }", "foreach (var (a12, a13) in e1) { A1(); }" }, { "a12", "a12" }, { "a13", "a13" }, { "{ A1(); }", "{ A1(); }" }, { "A1();", "A1();" }, { "foreach ((var b7, var b8, var b9) in e) { A2(); }", "foreach ((var b7, var b9) in e) { A2(); }" }, { "b7", "b7" }, { "b9", "b9" }, { "{ A2(); }", "{ A2(); }" }, { "A2();", "A2();" } }; expected.AssertEqual(actual); } #endregion #region Patterns [Fact] public void ConstantPattern() { var src1 = """ if ((o is null) && (y == 7)) return 3; if (a is 7) return 5; """; var src2 = """ if ((o1 is null) && (y == 7)) return 3; if (a is 77) return 5; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "if ((o is null) && (y == 7)) return 3;", "if ((o1 is null) && (y == 7)) return 3;" }, { "return 3;", "return 3;" }, { "if (a is 7) return 5;", "if (a is 77) return 5;" }, { "return 5;", "return 5;" } }; expected.AssertEqual(actual); } [Fact] public void DeclarationPattern() { var src1 = """ if (!(o is int i) && (y == 7)) return; if (!(a is string s)) return; if (!(b is string t)) return; if (!(c is int j)) return; """; var src2 = """ if (!(b is string t1)) return; if (!(o1 is int i) && (y == 7)) return; if (!(c is int)) return; if (!(a is int s)) return; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "if (!(o is int i) && (y == 7)) return;", "if (!(o1 is int i) && (y == 7)) return;" }, { "i", "i" }, { "return;", "return;" }, { "if (!(a is string s)) return;", "if (!(a is int s)) return;" }, { "s", "s" }, { "return;", "return;" }, { "if (!(b is string t)) return;", "if (!(b is string t1)) return;" }, { "t", "t1" }, { "return;", "return;" }, { "if (!(c is int j)) return;", "if (!(c is int)) return;" }, { "return;", "return;" } }; expected.AssertEqual(actual); } [Fact] public void VarPattern() { var src1 = """ if (!(o is (var x, var y))) return; if (!(o4 is (string a, var (b, c)))) return; if (!(o2 is var (e, f, g))) return; if (!(o3 is var (k, l, m))) return; """; var src2 = """ if (!(o is (int x, int y1))) return; if (!(o1 is (var a, (var b, string c1)))) return; if (!(o7 is var (g, e, f))) return; if (!(o3 is (string k, int l2, int m))) return; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "if (!(o is (var x, var y))) return;", "if (!(o is (int x, int y1))) return;" }, { "x", "x" }, { "y", "y1" }, { "return;", "return;" }, { "if (!(o4 is (string a, var (b, c)))) return;", "if (!(o1 is (var a, (var b, string c1)))) return;" }, { "a", "a" }, { "b", "b" }, { "c", "c1" }, { "return;", "return;" }, { "if (!(o2 is var (e, f, g))) return;", "if (!(o7 is var (g, e, f))) return;" }, { "e", "e" }, { "f", "f" }, { "g", "g" }, { "return;", "return;" }, { "if (!(o3 is var (k, l, m))) return;", "if (!(o3 is (string k, int l2, int m))) return;" }, { "k", "k" }, { "l", "l2" }, { "m", "m" }, { "return;", "return;" } }; expected.AssertEqual(actual); } [Fact] public void PositionalPattern() { var src1 = """ var r = (x, y, z) switch { (1, 2, 3) => 0, (var a, 3, 4) => a, (0, var b, int c) when c > 1 => 2, (1, 1, Point { X: 0 } p) => 3, _ => 4 }; """; var src2 = """ var r = ((x, y, z)) switch { (1, 2, 3) => 0, (var a1, 3, 4) => a1 * 2, (_, int b1, double c1) when c1 > 2 => c1, (1, 1, Point { Y: 0 } p1) => 3, _ => 4 }; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var r = (x, y, z) switch { (1, 2, 3) => 0, (var a, 3, 4) => a, (0, var b, int c) when c > 1 => 2, (1, 1, Point { X: 0 } p) => 3, _ => 4 };", "var r = ((x, y, z)) switch { (1, 2, 3) => 0, (var a1, 3, 4) => a1 * 2, (_, int b1, double c1) when c1 > 2 => c1, (1, 1, Point { Y: 0 } p1) => 3, _ => 4 };" }, { "var r = (x, y, z) switch { (1, 2, 3) => 0, (var a, 3, 4) => a, (0, var b, int c) when c > 1 => 2, (1, 1, Point { X: 0 } p) => 3, _ => 4 }", "var r = ((x, y, z)) switch { (1, 2, 3) => 0, (var a1, 3, 4) => a1 * 2, (_, int b1, double c1) when c1 > 2 => c1, (1, 1, Point { Y: 0 } p1) => 3, _ => 4 }" }, { "r = (x, y, z) switch { (1, 2, 3) => 0, (var a, 3, 4) => a, (0, var b, int c) when c > 1 => 2, (1, 1, Point { X: 0 } p) => 3, _ => 4 }", "r = ((x, y, z)) switch { (1, 2, 3) => 0, (var a1, 3, 4) => a1 * 2, (_, int b1, double c1) when c1 > 2 => c1, (1, 1, Point { Y: 0 } p1) => 3, _ => 4 }" }, { "(x, y, z) switch { (1, 2, 3) => 0, (var a, 3, 4) => a, (0, var b, int c) when c > 1 => 2, (1, 1, Point { X: 0 } p) => 3, _ => 4 }", "((x, y, z)) switch { (1, 2, 3) => 0, (var a1, 3, 4) => a1 * 2, (_, int b1, double c1) when c1 > 2 => c1, (1, 1, Point { Y: 0 } p1) => 3, _ => 4 }" }, { "(1, 2, 3) => 0", "(1, 2, 3) => 0" }, { "(var a, 3, 4) => a", "(var a1, 3, 4) => a1 * 2" }, { "a", "a1" }, { "(0, var b, int c) when c > 1 => 2", "(_, int b1, double c1) when c1 > 2 => c1" }, { "b", "c1" }, { "c", "b1" }, { "when c > 1", "when c1 > 2" }, { "(1, 1, Point { X: 0 } p) => 3", "(1, 1, Point { Y: 0 } p1) => 3" }, { "p", "p1" }, { "_ => 4", "_ => 4" } }; expected.AssertEqual(actual); } [Fact] public void PropertyPattern() { var src1 = """ if (address is { State: "WA" }) return 1; if (obj is { Color: Color.Purple }) return 2; if (o is string { Length: 5 } s) return 3; """; var src2 = """ if (address is { ZipCode: 98052 }) return 4; if (obj is { Size: Size.M }) return 2; if (o is string { Length: 7 } s7) return 5; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "if (address is { State: \"WA\" }) return 1;", "if (address is { ZipCode: 98052 }) return 4;" }, { "return 1;", "return 4;" }, { "if (obj is { Color: Color.Purple }) return 2;", "if (obj is { Size: Size.M }) return 2;" }, { "return 2;", "return 2;" }, { "if (o is string { Length: 5 } s) return 3;", "if (o is string { Length: 7 } s7) return 5;" }, { "s", "s7" }, { "return 3;", "return 5;" } }; expected.AssertEqual(actual); } [Fact] public void RecursivePatterns() { var src1 = """ var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { ("a", int i) => i, ("", Task<int> t) => await t, _ => 0 }, int i => i * i, _ => -1 }; """; var src2 = """ var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { ("b", decimal i1) => i1, ("", Task<object> obj2) => await obj2, _ => 0 }, double i => i * i, _ => -1 }; """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Async); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }, int i => i * i, _ => -1 };", "var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }, double i => i * i, _ => -1 };" }, { "var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }, int i => i * i, _ => -1 }", "var r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }, double i => i * i, _ => -1 }" }, { "r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }, int i => i * i, _ => -1 }", "r = obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }, double i => i * i, _ => -1 }" }, { "obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }, int i => i * i, _ => -1 }", "obj switch { string s when s.Length > 0 => (s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }, double i => i * i, _ => -1 }" }, { "string s when s.Length > 0 => (s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }", "string s when s.Length > 0 => (s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }" }, { "s", "s" }, { "when s.Length > 0", "when s.Length > 0" }, { "(s, obj1) switch { (\"a\", int i) => i, (\"\", Task<int> t) => await t, _ => 0 }", "(s, obj1) switch { (\"b\", decimal i1) => i1, (\"\", Task<object> obj2) => await obj2, _ => 0 }" }, { "(\"a\", int i) => i", "(\"b\", decimal i1) => i1" }, { "i", "i" }, { "(\"\", Task<int> t) => await t", "(\"\", Task<object> obj2) => await obj2" }, { "t", "obj2" }, { "await t", "await obj2" }, { "_ => 0", "_ => 0" }, { "int i => i * i", "double i => i * i" }, { "i", "i1" }, { "_ => -1", "_ => -1" } }; expected.AssertEqual(actual); } [Fact] public void CasePattern_UpdateInsert() { var src1 = """ switch(shape) { case Circle c: return 1; default: return 4; } """; var src2 = """ switch(shape) { case Circle c1: return 1; case Point p: return 0; default: return 4; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "switch(shape) { case Circle c: return 1; default: return 4; }", "switch(shape) { case Circle c1: return 1; case Point p: return 0; default: return 4; }" }, { "case Circle c: return 1;", "case Circle c1: return 1;" }, { "case Circle c:", "case Circle c1:" }, { "c", "c1" }, { "return 1;", "return 1;" }, { "default: return 4;", "default: return 4;" }, { "return 4;", "return 4;" } }; expected.AssertEqual(actual); } [Fact] public void CasePattern_UpdateDelete() { var src1 = """ switch(shape) { case Point p: return 0; case Circle c: return 1; } """; var src2 = """ switch(shape) { case Circle circle: return 1; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "switch(shape) { case Point p: return 0; case Circle c: return 1; }", "switch(shape) { case Circle circle: return 1; }" }, { "case Circle c: return 1;", "case Circle circle: return 1;" }, { "case Circle c:", "case Circle circle:" }, { "c", "circle" }, { "return 1;", "return 1;" } }; expected.AssertEqual(actual); } [Fact] public void WhenCondition() { var src1 = """ switch(shape) { case Circle c when (c < 10): return 1; case Circle c when (c > 100): return 2; } """; var src2 = """ switch(shape) { case Circle c when (c < 5): return 1; case Circle c2 when (c2 > 100): return 2; } """; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "switch(shape) { case Circle c when (c < 10): return 1; case Circle c when (c > 100): return 2; }", "switch(shape) { case Circle c when (c < 5): return 1; case Circle c2 when (c2 > 100): return 2; }" }, { "case Circle c when (c < 10): return 1;", "case Circle c when (c < 5): return 1;" }, { "case Circle c when (c < 10):", "case Circle c when (c < 5):" }, { "c", "c" }, { "when (c < 10)", "when (c < 5)" }, { "return 1;", "return 1;" }, { "case Circle c when (c > 100): return 2;", "case Circle c2 when (c2 > 100): return 2;" }, { "case Circle c when (c > 100):", "case Circle c2 when (c2 > 100):" }, { "c", "c2" }, { "when (c > 100)", "when (c2 > 100)" }, { "return 2;", "return 2;" } }; expected.AssertEqual(actual); } #endregion #region Switch Expression [Fact] public void SwitchExpressionArms_Lambda() { var src1 = @"F1() switch { 1 => new Func<int>(() => 1)(), _ => 2 };"; var src2 = @"F1() switch { 1 => new Func<int>(() => 3)(), _ => 2 };"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F1() switch { 1 => new Func<int>(() => 1)(), _ => 2 };", "F1() switch { 1 => new Func<int>(() => 3)(), _ => 2 };" }, { "F1() switch { 1 => new Func<int>(() => 1)(), _ => 2 }", "F1() switch { 1 => new Func<int>(() => 3)(), _ => 2 }" }, { "1 => new Func<int>(() => 1)()", "1 => new Func<int>(() => 3)()" }, { "() => 1", "() => 3" }, { "()", "()" }, { "_ => 2", "_ => 2" } }; expected.AssertEqual(actual); } [Fact] public void SwitchExpressionArms_NestedSimilar() { // The inner switch is mapped to the outer one, which is assumed to be removed. var src1 = @"F1() switch { 1 => 0, _ => F2() switch { 1 => 0, _ => 2 } };"; var src2 = @"F1() switch { 1 => 0, _ => 1 };"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "F1() switch { 1 => 0, _ => F2() switch { 1 => 0, _ => 2 } };", "F1() switch { 1 => 0, _ => 1 };" }, { "F2() switch { 1 => 0, _ => 2 }", "F1() switch { 1 => 0, _ => 1 }" }, { "1 => 0", "1 => 0" }, { "_ => 2", "_ => 1" } }; expected.AssertEqual(actual); } [Fact] public void SwitchExpressionArms_NestedDissimilar() { // The inner switch is mapped to the outer one, which is assumed to be removed. var src1 = @"Method() switch { true => G(), _ => F2() switch { 1 => 0, _ => 2 } };"; var src2 = @"Method() switch { true => G(), _ => 1 };"; var match = GetMethodMatches(src1, src2, kind: MethodKind.Regular); var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "Method() switch { true => G(), _ => F2() switch { 1 => 0, _ => 2 } };", "Method() switch { true => G(), _ => 1 };" }, { "Method() switch { true => G(), _ => F2() switch { 1 => 0, _ => 2 } }", "Method() switch { true => G(), _ => 1 }" }, { "true => G()", "true => G()" }, { "_ => F2() switch { 1 => 0, _ => 2 }", "_ => 1" } }; expected.AssertEqual(actual); } #endregion #region Top Level Statements [Fact] public void TopLevelStatements() { var src1 = """ Console.WriteLine(1); Console.WriteLine(2); var x = 0; while (true) { x++; } Console.WriteLine(3); """; var src2 = """ Console.WriteLine(4); Console.WriteLine(5); var x = 1; while (true) { x--; } Console.WriteLine(6); """; var match = GetTopEdits(src1, src2).Match; var actual = ToMatchingPairs(match); var expected = new MatchingPairs { { "Console.WriteLine(1);", "Console.WriteLine(4);" }, { "Console.WriteLine(2);", "Console.WriteLine(5);" }, { "var x = 0;", "var x = 1;" }, { "while (true) { x++; }", "while (true) { x--; }" }, { "Console.WriteLine(3);", "Console.WriteLine(6);" } }; expected.AssertEqual(actual); } #endregion }