提交 a2de1326 编写于 作者: I Ivan Basov 提交者: GitHub

support of C# 7.0 foreach in ENC (#19114)

上级 37c5398d
......@@ -7851,6 +7851,230 @@ .maxstack 2
IL_0012: ldloc.s V_6
IL_0014: ret
}
");
}
[Fact]
public void ForeachStatement()
{
var source0 = MarkedSource(@"
class C
{
public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) };
public static void G()
{
foreach (var (<N:0>x</N:0>, (<N:1>y</N:1>, <N:2>z</N:2>)) in F())
{
System.Console.WriteLine(x);
}
}
}");
var source1 = MarkedSource(@"
class C
{
public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) };
public static void G()
{
foreach (var (<N:0>x1</N:0>, (<N:1>y</N:1>, <N:2>z</N:2>)) in F())
{
System.Console.WriteLine(x1);
}
}
}");
var source2 = MarkedSource(@"
class C
{
public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) };
public static void G()
{
foreach (var (<N:0>x1</N:0>, <N:1>yz</N:1>) in F())
{
System.Console.WriteLine(x1);
}
}
}");
var compilation0 = CreateStandardCompilation(source0.Tree, options: ComSafeDebugDll, references: s_valueTupleRefs);
var compilation1 = compilation0.WithSource(source1.Tree);
var compilation2 = compilation1.WithSource(source2.Tree);
var f0 = compilation0.GetMember<MethodSymbol>("C.G");
var f1 = compilation1.GetMember<MethodSymbol>("C.G");
var f2 = compilation2.GetMember<MethodSymbol>("C.G");
var v0 = CompileAndVerify(compilation0);
v0.VerifyIL("C.G", @"
{
// Code size 72 (0x48)
.maxstack 2
.locals init ((int, (bool, double))[] V_0,
int V_1,
int V_2, //x
bool V_3, //y
double V_4, //z
System.ValueTuple<bool, double> V_5)
IL_0000: nop
IL_0001: nop
IL_0002: call ""(int, (bool, double))[] C.F()""
IL_0007: stloc.0
IL_0008: ldc.i4.0
IL_0009: stloc.1
IL_000a: br.s IL_0041
IL_000c: ldloc.0
IL_000d: ldloc.1
IL_000e: ldelem ""System.ValueTuple<int, (bool, double)>""
IL_0013: dup
IL_0014: ldfld ""(bool, double) System.ValueTuple<int, (bool, double)>.Item2""
IL_0019: stloc.s V_5
IL_001b: dup
IL_001c: ldfld ""int System.ValueTuple<int, (bool, double)>.Item1""
IL_0021: stloc.2
IL_0022: ldloc.s V_5
IL_0024: ldfld ""bool System.ValueTuple<bool, double>.Item1""
IL_0029: stloc.3
IL_002a: ldloc.s V_5
IL_002c: ldfld ""double System.ValueTuple<bool, double>.Item2""
IL_0031: stloc.s V_4
IL_0033: pop
IL_0034: nop
IL_0035: ldloc.2
IL_0036: call ""void System.Console.WriteLine(int)""
IL_003b: nop
IL_003c: nop
IL_003d: ldloc.1
IL_003e: ldc.i4.1
IL_003f: add
IL_0040: stloc.1
IL_0041: ldloc.1
IL_0042: ldloc.0
IL_0043: ldlen
IL_0044: conv.i4
IL_0045: blt.s IL_000c
IL_0047: ret
}
");
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(
new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
diff1.VerifyIL("C.G", @"
{
// Code size 80 (0x50)
.maxstack 2
.locals init ([unchanged] V_0,
[int] V_1,
int V_2, //x1
bool V_3, //y
double V_4, //z
[unchanged] V_5,
(int, (bool, double))[] V_6,
int V_7,
System.ValueTuple<bool, double> V_8)
IL_0000: nop
IL_0001: nop
IL_0002: call ""(int, (bool, double))[] C.F()""
IL_0007: stloc.s V_6
IL_0009: ldc.i4.0
IL_000a: stloc.s V_7
IL_000c: br.s IL_0047
IL_000e: ldloc.s V_6
IL_0010: ldloc.s V_7
IL_0012: ldelem ""System.ValueTuple<int, (bool, double)>""
IL_0017: dup
IL_0018: ldfld ""(bool, double) System.ValueTuple<int, (bool, double)>.Item2""
IL_001d: stloc.s V_8
IL_001f: dup
IL_0020: ldfld ""int System.ValueTuple<int, (bool, double)>.Item1""
IL_0025: stloc.2
IL_0026: ldloc.s V_8
IL_0028: ldfld ""bool System.ValueTuple<bool, double>.Item1""
IL_002d: stloc.3
IL_002e: ldloc.s V_8
IL_0030: ldfld ""double System.ValueTuple<bool, double>.Item2""
IL_0035: stloc.s V_4
IL_0037: pop
IL_0038: nop
IL_0039: ldloc.2
IL_003a: call ""void System.Console.WriteLine(int)""
IL_003f: nop
IL_0040: nop
IL_0041: ldloc.s V_7
IL_0043: ldc.i4.1
IL_0044: add
IL_0045: stloc.s V_7
IL_0047: ldloc.s V_7
IL_0049: ldloc.s V_6
IL_004b: ldlen
IL_004c: conv.i4
IL_004d: blt.s IL_000e
IL_004f: ret
}
");
var diff2 = compilation2.EmitDifference(
diff1.NextGeneration,
ImmutableArray.Create(
new SemanticEdit(SemanticEditKind.Update, f1, f2, GetSyntaxMapFromMarkers(source1, source2), preserveLocalVariables: true)));
diff2.VerifyIL("C.G", @"
{
// Code size 66 (0x42)
.maxstack 2
.locals init ([unchanged] V_0,
[int] V_1,
int V_2, //x1
[bool] V_3,
[unchanged] V_4,
[unchanged] V_5,
[unchanged] V_6,
[int] V_7,
[unchanged] V_8,
(int, (bool, double))[] V_9,
int V_10,
System.ValueTuple<bool, double> V_11, //yz
System.ValueTuple<int, (bool, double)> V_12)
IL_0000: nop
IL_0001: nop
IL_0002: call ""(int, (bool, double))[] C.F()""
IL_0007: stloc.s V_9
IL_0009: ldc.i4.0
IL_000a: stloc.s V_10
IL_000c: br.s IL_0039
IL_000e: ldloc.s V_9
IL_0010: ldloc.s V_10
IL_0012: ldelem ""System.ValueTuple<int, (bool, double)>""
IL_0017: stloc.s V_12
IL_0019: ldloc.s V_12
IL_001b: ldfld ""int System.ValueTuple<int, (bool, double)>.Item1""
IL_0020: stloc.2
IL_0021: ldloc.s V_12
IL_0023: ldfld ""(bool, double) System.ValueTuple<int, (bool, double)>.Item2""
IL_0028: stloc.s V_11
IL_002a: nop
IL_002b: ldloc.2
IL_002c: call ""void System.Console.WriteLine(int)""
IL_0031: nop
IL_0032: nop
IL_0033: ldloc.s V_10
IL_0035: ldc.i4.1
IL_0036: add
IL_0037: stloc.s V_10
IL_0039: ldloc.s V_10
IL_003b: ldloc.s V_9
IL_003d: ldlen
IL_003e: conv.i4
IL_003f: blt.s IL_000e
IL_0041: ret
}
");
}
}
......
......@@ -3433,6 +3433,92 @@ .maxstack 2
-IL_0025: ldloc.2
IL_0026: ret
}
", methodToken: diff1.UpdatedMethods.Single());
}
[Fact]
public void ForEachStatement_Deconstruction()
{
var source = @"
class C
{
public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) };
public static void G()
{
foreach (var (x, (y, z)) in F())
{
System.Console.WriteLine(x);
}
}
}";
var compilation0 = CreateStandardCompilation(source, options: TestOptions.DebugDll, references: s_valueTupleRefs);
var compilation1 = compilation0.WithSource(source);
var testData0 = new CompilationTestData();
var bytes0 = compilation0.EmitToArray(testData: testData0);
var methodData0 = testData0.GetMethodData("C.G");
var method0 = compilation0.GetMember<MethodSymbol>("C.G");
var generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), methodData0.EncDebugInfoProvider());
var method1 = compilation1.GetMember<MethodSymbol>("C.G");
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method0, method1, GetEquivalentNodesMap(method1, method0), preserveLocalVariables: true)));
diff1.VerifyIL("C.G", @"
{
// Code size 80 (0x50)
.maxstack 2
.locals init ([unchanged] V_0,
[int] V_1,
int V_2, //x
bool V_3, //y
double V_4, //z
[unchanged] V_5,
(int, (bool, double))[] V_6,
int V_7,
System.ValueTuple<bool, double> V_8)
-IL_0000: nop
-IL_0001: nop
-IL_0002: call ""(int, (bool, double))[] C.F()""
IL_0007: stloc.s V_6
IL_0009: ldc.i4.0
IL_000a: stloc.s V_7
~IL_000c: br.s IL_0047
-IL_000e: ldloc.s V_6
IL_0010: ldloc.s V_7
IL_0012: ldelem ""System.ValueTuple<int, (bool, double)>""
IL_0017: dup
IL_0018: ldfld ""(bool, double) System.ValueTuple<int, (bool, double)>.Item2""
IL_001d: stloc.s V_8
IL_001f: dup
IL_0020: ldfld ""int System.ValueTuple<int, (bool, double)>.Item1""
IL_0025: stloc.2
IL_0026: ldloc.s V_8
IL_0028: ldfld ""bool System.ValueTuple<bool, double>.Item1""
IL_002d: stloc.3
IL_002e: ldloc.s V_8
IL_0030: ldfld ""double System.ValueTuple<bool, double>.Item2""
IL_0035: stloc.s V_4
IL_0037: pop
-IL_0038: nop
-IL_0039: ldloc.2
IL_003a: call ""void System.Console.WriteLine(int)""
IL_003f: nop
-IL_0040: nop
~IL_0041: ldloc.s V_7
IL_0043: ldc.i4.1
IL_0044: add
IL_0045: stloc.s V_7
-IL_0047: ldloc.s V_7
IL_0049: ldloc.s V_6
IL_004b: ldlen
IL_004c: conv.i4
IL_004d: blt.s IL_000e
-IL_004f: ret
}
", methodToken: diff1.UpdatedMethods.Single());
}
}
......
......@@ -3184,6 +3184,41 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariableBody_Update_ExpressionActive()
{
string src1 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach ((string s, int i) in <AS:1>F()</AS:1>)
{
System.Console.Write(0);
}
}
}";
string src2 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach ((string s, int i) in <AS:1>F()</AS:1>)
{
System.Console.Write(1);
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachBody_Update_InKeywordActive()
{
......@@ -3219,6 +3254,41 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariableBody_Update_InKeywordActive()
{
string src1 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach ((string s, int i) <AS:1>in</AS:1> F())
{
System.Console.Write(0);
}
}
}";
string src2 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach ((string s, int i) <AS:1>in</AS:1> F())
{
System.Console.Write(1);
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachBody_Update_VariableActive()
{
......@@ -3254,6 +3324,41 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariableBody_Update_VariableActive()
{
string src1 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach (<AS:1>(string s, int i)</AS:1> in F())
{
System.Console.Write(0);
}
}
}";
string src2 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
foreach (<AS:1>(string s, int i)</AS:1> in F())
{
System.Console.Write(1);
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachBody_Update_ForeachKeywordActive()
{
......@@ -3289,6 +3394,41 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariableBody_Update_ForeachKeywordActive()
{
string src1 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
<AS:1>foreach</AS:1> ((string s, int i) in F())
{
System.Console.Write(0);
}
}
}";
string src2 = @"
class Test
{
private static (string, int) F() { <AS:0>return null;</AS:0> }
static void Main(string[] args)
{
<AS:1>foreach</AS:1> ((string s, int i) in F())
{
System.Console.Write(1);
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariable_Update()
{
......@@ -3328,7 +3468,44 @@ static void Main(string[] args)
}
[Fact]
public void ForEach_Reorder_Leaf1()
public void ForEachDeconstructionVariable_Update()
{
string src1 = @"
class Test
{
private static (int, (bool, double))[] F() { <AS:0>return new[] { (1, (true, 2.0)) };</AS:0> }
static void Main(string[] args)
{
foreach (<AS:1>(int i, (bool b, double d))</AS:1> in F())
{
System.Console.Write(0);
}
}
}";
string src2 = @"
class Test
{
private static (int, (bool, double))[] F() { <AS:0>return new[] { (1, (true, 2.0)) };</AS:0> }
static void Main(string[] args)
{
foreach (<AS:1>(int i, (var b, double d))</AS:1> in F())
{
System.Console.Write(1);
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementUpdate, "(int i, (var b, double d))"),
Diagnostic(RudeEditKind.UpdateAroundActiveStatement, "foreach ( (int i, (var b, double d)) in F())", CSharpFeaturesResources.foreach_statement));
}
[Fact]
public void ForEach_Reorder_Leaf()
{
string src1 = @"
class Test
......@@ -3377,19 +3554,19 @@ static void Main(string[] args)
}
[Fact]
public void ForEach_Update_Leaf1()
public void ForEachVariable_Reorder_Leaf()
{
string src1 = @"
class Test
{
public static int[] e1 = new int[1];
public static int[] e2 = new int[1];
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach (var a in e1)
foreach ((var a1, var a2) in e1)
{
foreach (var b in e1)
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
......@@ -3402,16 +3579,16 @@ static void Main(string[] args)
string src2 = @"
class Test
{
public static int[] e1 = new int[1];
public static int[] e2 = new int[1];
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach (var b in e1)
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
foreach (var a in e1)
foreach ((var a1, var a2) in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
......@@ -3426,7 +3603,7 @@ static void Main(string[] args)
}
[Fact]
public void ForEach_Update_Leaf2()
public void ForEach_Update_Leaf()
{
string src1 = @"
class Test
......@@ -3468,6 +3645,49 @@ static void Main(string[] args)
Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach (var a in e1)", CSharpFeaturesResources.foreach_statement));
}
[Fact]
public void ForEachVariable_Update_Leaf()
{
string src1 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
<AS:0>System.Console.Write();</AS:0>
}
}";
string src2 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
foreach ((var a1, var a2) in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach (var c in e1)", CSharpFeaturesResources.foreach_statement),
Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach ((int b1, bool b2) in e1)", CSharpFeaturesResources.foreach_statement),
Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach ((var a1, var a2) in e1)", CSharpFeaturesResources.foreach_statement));
}
[Fact]
public void ForEach_Delete_Leaf1()
{
......@@ -3514,6 +3734,52 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariable_Delete_Leaf1()
{
string src1 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach ((var a1, var a2) in e1)
{
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}
}";
string src2 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach ((var a1, var a2) in e1)
{
foreach ((int b1, bool b2) in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEach_Delete_Leaf2()
{
......@@ -3560,6 +3826,52 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariable_Delete_Leaf2()
{
string src1 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach ((var a1, var a2) in e1)
{
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}
}";
string src2 = @"
class Test
{
public static (int, bool)[] e1 = new (int, bool)[1];
public static (int, bool)[] e2 = new (int, bool)[1];
static void Main(string[] args)
{
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEach_Delete_Leaf3()
{
......@@ -3606,6 +3918,52 @@ static void Main(string[] args)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEachVariable_Delete_Leaf3()
{
string src1 = @"
class Test
{
public static int[] e1 = new int[1];
public static int[] e2 = new int[1];
static void Main(string[] args)
{
foreach ((var a1, var a2) in e1)
{
foreach ((int b1, bool b2) in e1)
{
foreach (var c in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}
}";
string src2 = @"
class Test
{
public static int[] e1 = new int[1];
public static int[] e2 = new int[1];
static void Main(string[] args)
{
foreach ((var a1, var a2) in e1)
{
foreach (var c in e1)
{
<AS:0>System.Console.Write();</AS:0>
}
}
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void ForEach_Lambda1()
{
......@@ -8182,8 +8540,7 @@ static void F(object o)
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.UpdateAroundActiveStatement, "var (x, y)", CSharpFeaturesResources.deconstruction));
edits.VerifyRudeDiagnostics(active);
}
[Fact]
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
......@@ -306,7 +305,8 @@ protected override SyntaxNode FindStatementAndPartner(SyntaxNode declarationBody
break;
case SyntaxKind.ForEachStatement:
statementPart = (int)GetStatementPart((ForEachStatementSyntax)node, position);
case SyntaxKind.ForEachVariableStatement:
statementPart = (int)GetStatementPart((CommonForEachStatementSyntax)node, position);
break;
case SyntaxKind.VariableDeclaration:
......@@ -352,7 +352,7 @@ private static TextSpan GetActiveSpan(BlockSyntax node, BlockPart part)
}
}
private static ForEachPart GetStatementPart(ForEachStatementSyntax node, int position)
private static ForEachPart GetStatementPart(CommonForEachStatementSyntax node, int position)
{
return position < node.OpenParenToken.SpanStart ? ForEachPart.ForEach :
position < node.InKeyword.SpanStart ? ForEachPart.VariableDeclaration :
......@@ -381,6 +381,27 @@ private static TextSpan GetActiveSpan(ForEachStatementSyntax node, ForEachPart p
}
}
private static TextSpan GetActiveSpan(ForEachVariableStatementSyntax node, ForEachPart part)
{
switch (part)
{
case ForEachPart.ForEach:
return node.ForEachKeyword.Span;
case ForEachPart.VariableDeclaration:
return TextSpan.FromBounds(node.Variable.SpanStart, node.Variable.Span.End);
case ForEachPart.In:
return node.InKeyword.Span;
case ForEachPart.Expression:
return node.Expression.Span;
default:
throw ExceptionUtilities.UnexpectedValue(part);
}
}
protected override bool AreEquivalent(SyntaxNode left, SyntaxNode right)
{
return SyntaxFactory.AreEquivalent(left, right);
......@@ -601,6 +622,10 @@ protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out
span = GetActiveSpan((ForEachStatementSyntax)node, (ForEachPart)statementPart);
return true;
case SyntaxKind.ForEachVariableStatement:
span = GetActiveSpan((ForEachVariableStatementSyntax)node, (ForEachPart)statementPart);
return true;
case SyntaxKind.DoStatement:
// The active statement of DoStatement node is the while condition,
// which is lexically not the closest breakpoint span (the body is).
......@@ -732,10 +757,11 @@ protected override bool AreEquivalentActiveStatements(SyntaxNode oldStatement, S
return true;
case SyntaxKind.ForEachStatement:
case SyntaxKind.ForEachVariableStatement:
Debug.Assert(statementPart != 0);
// only check the expression, edits in the body and the variable declaration are allowed:
return AreEquivalentActiveStatements((ForEachStatementSyntax)oldStatement, (ForEachStatementSyntax)newStatement);
return AreEquivalentActiveStatements((CommonForEachStatementSyntax)oldStatement, (CommonForEachStatementSyntax)newStatement);
case SyntaxKind.IfStatement:
// only check the condition, edits in the body are allowed:
......@@ -807,11 +833,30 @@ private static bool AreEquivalentActiveStatements(UsingStatementSyntax oldNode,
(SyntaxNode)newNode.Declaration ?? newNode.Expression);
}
private static bool AreEquivalentActiveStatements(ForEachStatementSyntax oldNode, ForEachStatementSyntax newNode)
private static bool AreEquivalentActiveStatements(CommonForEachStatementSyntax oldNode, CommonForEachStatementSyntax newNode)
{
// This is conservative, we might be able to allow changing the type.
return AreEquivalentIgnoringLambdaBodies(oldNode.Type, newNode.Type)
&& AreEquivalentIgnoringLambdaBodies(oldNode.Expression, newNode.Expression);
if (oldNode.Kind() != newNode.Kind() || !AreEquivalentIgnoringLambdaBodies(oldNode.Expression, newNode.Expression))
{
return false;
}
switch (oldNode.Kind())
{
case SyntaxKind.ForEachStatement: return AreEquivalentIgnoringLambdaBodies(((ForEachStatementSyntax)oldNode).Type, ((ForEachStatementSyntax)newNode).Type);
case SyntaxKind.ForEachVariableStatement: return AreEquivalentIgnoringLambdaBodies(((ForEachVariableStatementSyntax)oldNode).Variable, ((ForEachVariableStatementSyntax)newNode).Variable);
default: throw ExceptionUtilities.UnexpectedValue(oldNode.Kind());
}
}
private static bool AreSimilarActiveStatements(CommonForEachStatementSyntax oldNode, CommonForEachStatementSyntax newNode)
{
List<SyntaxToken> oldTokens = null;
List<SyntaxToken> newTokens = null;
StatementSyntaxComparer.GetLocalNames(oldNode, ref oldTokens);
StatementSyntaxComparer.GetLocalNames(newNode, ref newTokens);
return DeclareSameIdentifiers(oldTokens.ToArray(), newTokens.ToArray());
}
internal override bool IsMethod(SyntaxNode declaration)
......@@ -1259,8 +1304,9 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node,
return TextSpan.FromBounds(forStatement.ForKeyword.SpanStart, forStatement.CloseParenToken.Span.End);
case SyntaxKind.ForEachStatement:
var forEachStatement = (ForEachStatementSyntax)node;
return TextSpan.FromBounds(forEachStatement.ForEachKeyword.SpanStart, forEachStatement.CloseParenToken.Span.End);
case SyntaxKind.ForEachVariableStatement:
var commonForEachStatement = (CommonForEachStatementSyntax)node;
return TextSpan.FromBounds(commonForEachStatement.ForEachKeyword.SpanStart, commonForEachStatement.CloseParenToken.Span.End);
case SyntaxKind.LabeledStatement:
return ((LabeledStatementSyntax)node).Identifier.Span;
......@@ -1342,9 +1388,6 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node,
case SyntaxKind.GroupClause:
return ((GroupClauseSyntax)node).GroupKeyword.Span;
case SyntaxKind.ForEachVariableStatement:
return ((ForEachVariableStatementSyntax)node).Variable.Span;
case SyntaxKind.IsPatternExpression:
case SyntaxKind.TupleType:
case SyntaxKind.TupleExpression:
......@@ -1549,6 +1592,7 @@ internal static string GetStatementDisplayNameImpl(SyntaxNode node)
return CSharpFeaturesResources.lock_statement;
case SyntaxKind.ForEachStatement:
case SyntaxKind.ForEachVariableStatement:
return CSharpFeaturesResources.foreach_statement;
case SyntaxKind.CheckedStatement:
......@@ -1604,9 +1648,6 @@ internal static string GetStatementDisplayNameImpl(SyntaxNode node)
case SyntaxKind.IsPatternExpression:
return CSharpFeaturesResources.is_pattern;
case SyntaxKind.ForEachVariableStatement:
return CSharpFeaturesResources.deconstruction;
case SyntaxKind.SimpleAssignmentExpression:
if (((AssignmentExpressionSyntax)node).IsDeconstruction())
{
......@@ -3108,7 +3149,6 @@ private static bool IsUnsupportedCSharp7EnCNode(SyntaxNode n)
{
switch (n.Kind())
{
case SyntaxKind.ForEachVariableStatement:
case SyntaxKind.LocalFunctionStatement:
return true;
default:
......@@ -3187,15 +3227,15 @@ private static CheckedStatementSyntax TryGetCheckedStatementAncestor(SyntaxNode
//
// Unlike exception regions matching where we use LCS, we allow reordering of the statements.
ReportUnmatchedStatements<LockStatementSyntax>(diagnostics, match, (int)SyntaxKind.LockStatement, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements<LockStatementSyntax>(diagnostics, match, new[] { (int)SyntaxKind.LockStatement }, oldActiveStatement, newActiveStatement,
areEquivalent: AreEquivalentActiveStatements,
areSimilar: null);
ReportUnmatchedStatements<FixedStatementSyntax>(diagnostics, match, (int)SyntaxKind.FixedStatement, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements<FixedStatementSyntax>(diagnostics, match, new[] { (int)SyntaxKind.FixedStatement }, oldActiveStatement, newActiveStatement,
areEquivalent: AreEquivalentActiveStatements,
areSimilar: (n1, n2) => DeclareSameIdentifiers(n1.Declaration.Variables, n2.Declaration.Variables));
ReportUnmatchedStatements<UsingStatementSyntax>(diagnostics, match, (int)SyntaxKind.UsingStatement, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements<UsingStatementSyntax>(diagnostics, match, new[] { (int)SyntaxKind.UsingStatement }, oldActiveStatement, newActiveStatement,
areEquivalent: AreEquivalentActiveStatements,
areSimilar: (using1, using2) =>
{
......@@ -3203,21 +3243,26 @@ private static CheckedStatementSyntax TryGetCheckedStatementAncestor(SyntaxNode
DeclareSameIdentifiers(using1.Declaration.Variables, using2.Declaration.Variables);
});
ReportUnmatchedStatements<ForEachStatementSyntax>(diagnostics, match, (int)SyntaxKind.ForEachStatement, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements<CommonForEachStatementSyntax>(diagnostics, match, new[] { (int)SyntaxKind.ForEachStatement, (int)SyntaxKind.ForEachVariableStatement }, oldActiveStatement, newActiveStatement,
areEquivalent: AreEquivalentActiveStatements,
areSimilar: (n1, n2) => SyntaxFactory.AreEquivalent(n1.Identifier, n2.Identifier));
areSimilar: AreSimilarActiveStatements);
}
private static bool DeclareSameIdentifiers(SeparatedSyntaxList<VariableDeclaratorSyntax> oldVariables, SeparatedSyntaxList<VariableDeclaratorSyntax> newVariables)
{
if (oldVariables.Count != newVariables.Count)
return DeclareSameIdentifiers(oldVariables.Select(v => v.Identifier).ToArray(), newVariables.Select(v => v.Identifier).ToArray());
}
private static bool DeclareSameIdentifiers(SyntaxToken[] oldVariables, SyntaxToken[] newVariables)
{
if (oldVariables.Length != newVariables.Length)
{
return false;
}
for (int i = 0; i < oldVariables.Count; i++)
for (int i = 0; i < oldVariables.Length; i++)
{
if (!SyntaxFactory.AreEquivalent(oldVariables[i].Identifier, newVariables[i].Identifier))
if (!SyntaxFactory.AreEquivalent(oldVariables[i], newVariables[i]))
{
return false;
}
......
......@@ -1006,7 +1006,7 @@ private static void GetLocalNames(VariableDeclarationSyntax localDeclaration, re
}
}
private static void GetLocalNames(CommonForEachStatementSyntax commonForEach, ref List<SyntaxToken> result)
internal static void GetLocalNames(CommonForEachStatementSyntax commonForEach, ref List<SyntaxToken> result)
{
switch (commonForEach.Kind())
{
......
......@@ -1696,7 +1696,7 @@ protected void AddRudeDeleteAroundActiveStatement(List<RudeEditDiagnostic> diagn
protected void ReportUnmatchedStatements<TSyntaxNode>(
List<RudeEditDiagnostic> diagnostics,
Match<SyntaxNode> match,
int syntaxKind,
int[] syntaxKinds,
SyntaxNode oldActiveStatement,
SyntaxNode newActiveStatement,
Func<TSyntaxNode, TSyntaxNode, bool> areEquivalent,
......@@ -1704,8 +1704,8 @@ protected void AddRudeDeleteAroundActiveStatement(List<RudeEditDiagnostic> diagn
where TSyntaxNode : SyntaxNode
{
List<SyntaxNode> oldNodes = null, newNodes = null;
GetAncestors(GetEncompassingAncestor(match.OldRoot), oldActiveStatement, syntaxKind, ref oldNodes);
GetAncestors(GetEncompassingAncestor(match.NewRoot), newActiveStatement, syntaxKind, ref newNodes);
GetAncestors(GetEncompassingAncestor(match.OldRoot), oldActiveStatement, syntaxKinds, ref oldNodes);
GetAncestors(GetEncompassingAncestor(match.NewRoot), newActiveStatement, syntaxKinds, ref newNodes);
if (newNodes != null)
{
......@@ -1836,11 +1836,11 @@ private static int IndexOfEquivalent<TSyntaxNode>(SyntaxNode newNode, List<Synta
return -1;
}
private static void GetAncestors(SyntaxNode root, SyntaxNode node, int syntaxKind, ref List<SyntaxNode> list)
private static void GetAncestors(SyntaxNode root, SyntaxNode node, int[] syntaxKinds, ref List<SyntaxNode> list)
{
while (node != root)
{
if (node.RawKind == syntaxKind)
if (syntaxKinds.Contains(node.RawKind))
{
if (list == null)
{
......
......@@ -3155,19 +3155,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
'
' Unlike exception regions matching where we use LCS, we allow reordering of the statements.
ReportUnmatchedStatements(Of SyncLockBlockSyntax)(diagnostics, match, SyntaxKind.SyncLockBlock, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements(Of SyncLockBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.SyncLockBlock}, oldActiveStatement, newActiveStatement,
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.SyncLockStatement.Expression, n2.SyncLockStatement.Expression),
areSimilar:=Nothing)
ReportUnmatchedStatements(Of WithBlockSyntax)(diagnostics, match, SyntaxKind.WithBlock, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements(Of WithBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.WithBlock}, oldActiveStatement, newActiveStatement,
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.WithStatement.Expression, n2.WithStatement.Expression),
areSimilar:=Nothing)
ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, match, SyntaxKind.UsingBlock, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.UsingBlock}, oldActiveStatement, newActiveStatement,
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.UsingStatement.Expression, n2.UsingStatement.Expression),
areSimilar:=Nothing)
ReportUnmatchedStatements(Of ForOrForEachBlockSyntax)(diagnostics, match, SyntaxKind.ForEachBlock, oldActiveStatement, newActiveStatement,
ReportUnmatchedStatements(Of ForOrForEachBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.ForEachBlock}, oldActiveStatement, newActiveStatement,
areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.ForOrForEachStatement, n2.ForOrForEachStatement),
areSimilar:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(DirectCast(n1.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable,
DirectCast(n2.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable))
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册