提交 c90f3a12 编写于 作者: T Tomas Matousek

Implement EnC support for range variables in C# queries, improve query clause matching

上级 9c9a01d7
......@@ -590,7 +590,10 @@ private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, Diagnos
}
var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d);
return lambdaBodyBinder.CreateBlockFromExpression(let, lambdaBodyBinder.Locals, null, construction, d);
// The bound block represents a closure scope for transparent identifiers captured in the let clause.
// Such closures shall be associated with the lambda body expression.
return lambdaBodyBinder.CreateBlockFromExpression(let.Expression, lambdaBodyBinder.Locals, null, construction, d);
};
var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x), let.Expression, bodyFactory);
......
......@@ -70,10 +70,6 @@ internal static bool IsClosureScope(SyntaxNode node)
case SyntaxKind.ForEachStatement:
case SyntaxKind.UsingStatement:
// variable captured by a lambda in a let clause,
// e.g. from item in array let a = new Func<int>(() => item)
case SyntaxKind.LetClause:
// ctor parameter captured by a lambda in a ctor initializer
case SyntaxKind.ConstructorDeclaration:
return true;
......
......@@ -497,6 +497,422 @@ void F()
Row(21, TableIndex.MethodDef, EditAndContinueOperation.Default));
}
[Fact]
public void TransparentIdentifiers_FromClause()
{
var source0 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
void F()
{
var result = <N:0>from a in new[] { 1 }</N:0>
<N:1>from b in new[] { 1 }</N:1>
<N:2>where <N:7>Z(<N:5>() => a</N:5>) > 0</N:7></N:2>
<N:3>where <N:8>Z(<N:6>() => b</N:7>) > 0</N:8></N:3>
<N:4>select a</N:4>;
}
}");
var source1 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
void F()
{
var result = <N:0>from a in new[] { 1 }</N:0>
<N:1>from b in new[] { 2 }</N:1>
<N:2>where <N:7>Z(<N:5>() => a</N:5>) > 1</N:7></N:2>
<N:3>where <N:8>Z(<N:6>() => b</N:7>) > 2</N:8></N:3>
<N:4>select a</N:4>;
}
}");
var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var compilation1 = compilation0.WithSource(source1.Tree);
var v0 = CompileAndVerify(compilation0);
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var f0 = compilation0.GetMember<MethodSymbol>("C.F");
var f1 = compilation1.GetMember<MethodSymbol>("C.F");
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreatePdbInfoProvider().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
// no new synthesized members generated (with #1 in names):
diff1.VerifySynthesizedMembers(
"C: {<F>b__1_2, <F>b__1_4, <>c__DisplayClass1_0, <>c__DisplayClass1_1, <>c}",
"C.<>c: {<>9__1_0, <>9__1_1, <>9__1_6, <F>b__1_0, <F>b__1_1, <F>b__1_6}",
"C.<>c__DisplayClass1_0: {<>h__TransparentIdentifier0, <F>b__3}",
"C.<>c__DisplayClass1_1: {<>h__TransparentIdentifier0, <F>b__5}",
"<>f__AnonymousType0<<a>j__TPar, <b>j__TPar>: {Equals, GetHashCode, ToString}");
var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
// Method updates for lambdas:
CheckEncLogDefinitions(reader1,
Row(8, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(9, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(10, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(11, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(12, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(13, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(8, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(11, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(13, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(15, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(18, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(19, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(20, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(17, TableIndex.CustomAttribute, EditAndContinueOperation.Default),
Row(18, TableIndex.CustomAttribute, EditAndContinueOperation.Default));
}
[Fact]
public void TransparentIdentifiers_LetClause()
{
var source0 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
void F()
{
var result = <N:0>from a in new[] { 1 }</N:0>
<N:1>let b = <N:2>Z(<N:3>() => a</N:3>)</N:2></N:1>
<N:4>select <N:5>a + b</N:5></N:4>;
}
}");
var source1 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
void F()
{
var result = <N:0>from a in new[] { 1 }</N:0>
<N:1>let b = <N:2>Z(<N:3>() => a</N:3>) + 1</N:2></N:1>
<N:4>select <N:5>a + b</N:5></N:4>;
}
}");
var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var compilation1 = compilation0.WithSource(source1.Tree);
var v0 = CompileAndVerify(compilation0);
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var f0 = compilation0.GetMember<MethodSymbol>("C.F");
var f1 = compilation1.GetMember<MethodSymbol>("C.F");
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreatePdbInfoProvider().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
// no new synthesized members generated (with #1 in names):
diff1.VerifySynthesizedMembers(
"C: {<F>b__1_0, <>c__DisplayClass1_0, <>c}",
"C.<>c: {<>9__1_2, <F>b__1_2}",
"C.<>c__DisplayClass1_0: {a, <F>b__1}",
"<>f__AnonymousType0<<a>j__TPar, <b>j__TPar>: {Equals, GetHashCode, ToString}");
var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
// Method updates for lambdas:
CheckEncLogDefinitions(reader1,
Row(6, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(7, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(8, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(8, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(15, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(15, TableIndex.CustomAttribute, EditAndContinueOperation.Default));
}
[Fact]
public void TransparentIdentifiers_JoinClause()
{
var source0 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>join b in new[] { 3 } on
<N:3>Z(<N:4>() => <N:5>a + 1</N:5></N:4>)</N:3>
equals
<N:6>Z(<N:7>() => <N:8>b - 1</N:8></N:7>)</N:6></N:2>
<N:9>select <N:10>Z(<N:11>() => <N:12>a + b</N:12></N:11>)</N:10></N:9>;
}
}");
var source1 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>join b in new[] { 3 } on
<N:3>Z(<N:4>() => <N:5>a + 1</N:5></N:4>)</N:3>
equals
<N:6>Z(<N:7>() => <N:8>b - 1</N:8></N:7>)</N:6></N:2>
<N:9>select <N:10>Z(<N:11>() => <N:12>a - b</N:12></N:11>)</N:10></N:9>;
}
}");
var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var compilation1 = compilation0.WithSource(source1.Tree);
var v0 = CompileAndVerify(compilation0);
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var f0 = compilation0.GetMember<MethodSymbol>("C.F");
var f1 = compilation1.GetMember<MethodSymbol>("C.F");
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreatePdbInfoProvider().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
// no new synthesized members generated (with #1 in names):
diff1.VerifySynthesizedMembers(
"C.<>c__DisplayClass1_0: {a, <F>b__1}",
"C.<>c__DisplayClass1_1: {b, <F>b__3}",
"C.<>c__DisplayClass1_2: {a, b, <F>b__5}",
"C: {<F>b__1_0, <F>b__1_2, <F>b__1_4, <>c__DisplayClass1_0, <>c__DisplayClass1_1, <>c__DisplayClass1_2}");
var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
// Method updates for lambdas:
CheckEncLogDefinitions(reader1,
Row(6, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(7, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(8, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(9, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(10, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(5, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(8, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(12, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(10, TableIndex.CustomAttribute, EditAndContinueOperation.Default),
Row(11, TableIndex.CustomAttribute, EditAndContinueOperation.Default),
Row(12, TableIndex.CustomAttribute, EditAndContinueOperation.Default));
}
[Fact]
public void TransparentIdentifiers_JoinIntoClause()
{
var source0 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>join b in new[] { 3 } on
<N:3>a + 1</N:3> equals <N:4>b - 1</N:4>
into g</N:2>
<N:5>select <N:6>Z(<N:7>() => <N:8>g.First()</N:8></N:7>)</N:6></N:5>;
}
}");
var source1 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>join b in new[] { 3 } on
<N:3>a + 1</N:3> equals <N:4>b - 1</N:4>
into g</N:2>
<N:5>select <N:6>Z(<N:7>() => <N:8>g.Last()</N:8></N:7>)</N:6></N:5>;
}
}");
var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var compilation1 = compilation0.WithSource(source1.Tree);
var v0 = CompileAndVerify(compilation0);
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var f0 = compilation0.GetMember<MethodSymbol>("C.F");
var f1 = compilation1.GetMember<MethodSymbol>("C.F");
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreatePdbInfoProvider().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
// no new synthesized members generated (with #1 in names):
diff1.VerifySynthesizedMembers(
"C: {<F>b__1_2, <>c__DisplayClass1_0, <>c}",
"C.<>c: {<>9__1_0, <>9__1_1, <F>b__1_0, <F>b__1_1}",
"C.<>c__DisplayClass1_0: {g, <F>b__3}");
var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
// Method updates for lambdas:
CheckEncLogDefinitions(reader1,
Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(5, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(6, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(10, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default));
}
[Fact]
public void TransparentIdentifiers_QueryContinuation()
{
var source0 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>group a by <N:3>a + 1</N:3></N:2>
<N:4>into g
<N:5>select <N:6>Z(<N:7>() => <N:8>g.First()</N:8></N:7>)</N:6></N:5></N:4>;
}
}");
var source1 = MarkedSource(@"
using System;
using System.Linq;
class C
{
int Z(Func<int> f)
{
return 1;
}
public void F()
{
var result = <N:0>from a in <N:1>new[] { 1 }</N:1></N:0>
<N:2>group a by <N:3>a + 1</N:3></N:2>
<N:4>into g
<N:5>select <N:6>Z(<N:7>() => <N:8>g.Last()</N:8></N:7>)</N:6></N:5></N:4>;
}
}");
var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll.WithMetadataImportOptions(MetadataImportOptions.All));
var compilation1 = compilation0.WithSource(source1.Tree);
var v0 = CompileAndVerify(compilation0);
var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData);
var f0 = compilation0.GetMember<MethodSymbol>("C.F");
var f1 = compilation1.GetMember<MethodSymbol>("C.F");
var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreatePdbInfoProvider().GetEncMethodDebugInfo);
var diff1 = compilation1.EmitDifference(
generation0,
ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true)));
// no new synthesized members generated (with #1 in names):
diff1.VerifySynthesizedMembers(
"C: {<F>b__1_1, <>c__DisplayClass1_0, <>c}",
"C.<>c: {<>9__1_0, <F>b__1_0}",
"C.<>c__DisplayClass1_0: {g, <F>b__2}");
var md1 = diff1.GetMetadata();
var reader1 = md1.Reader;
// Method updates for lambdas:
CheckEncLogDefinitions(reader1,
Row(4, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(5, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(6, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(4, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(6, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(9, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default));
}
[Fact]
public void UniqueSynthesizedNames1()
{
......
......@@ -6215,7 +6215,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select b.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select b.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6224,7 +6224,7 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select b.bar", "where clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "where clause"));
}
[Fact]
......@@ -6244,7 +6244,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select a.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select a.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6253,7 +6253,7 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select a.bar", "let clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "let clause"));
}
[Fact]
......@@ -6276,7 +6276,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select a.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select a.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6285,7 +6285,7 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select a.bar", "join clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "join clause"));
}
[Fact]
......@@ -6308,7 +6308,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select a.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select a.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6317,7 +6317,7 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select a.bar", "orderby clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "orderby clause"));
}
[Fact]
......@@ -6340,7 +6340,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select a.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select a.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6349,7 +6349,7 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select a.bar", "orderby clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "orderby clause"));
}
[Fact]
......@@ -6372,7 +6372,7 @@ class C
{
static void Main(string[] args)
{
var <AS:0>s = from a in b select a.bar</AS:0>;
var s = <AS:0>from</AS:0> a in b select a.bar;
<AS:1>s.ToArray();</AS:1>
}
}
......@@ -6381,7 +6381,100 @@ static void Main(string[] args)
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "s = from a in b select a.bar", "orderby clause"));
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "from", "orderby clause"));
}
[Fact]
public void Queries_Remove_JoinInto1()
{
string src1 = @"
class C
{
static void Main()
{
var q = from x in xs
join y in ys on F() equals G() into g
select <AS:0>1</AS:0>;
}
}";
string src2 = @"
class C
{
static void Main()
{
var q = from x in xs
join y in ys on F() equals G()
select <AS:0>1</AS:0>;
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void Queries_Remove_QueryContinuation1()
{
string src1 = @"
class C
{
static void Main()
{
var q = from x in xs
group x by x.F() into g
where <AS:0>g.F()</AS:0>
select 1;
}
}";
string src2 = @"
class C
{
static void Main()
{
var q = from x in xs
group x by x.F() <AS:0>into</AS:0> g
select 1;
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "into", "where clause"));
}
[Fact]
public void Queries_Remove_QueryContinuation2()
{
string src1 = @"
class C
{
static void Main()
{
var q = from x in xs
group x by x.F() into g
select <AS:0>1</AS:0>;
}
}";
string src2 = @"
class C
{
static void Main()
{
var q = from x in xs
<AS:0>join</AS:0> y in ys on F() equals G() into g
select 1;
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "join", "select clause"));
}
#endregion
......
......@@ -13,7 +13,7 @@ internal static class Extensions
public static void Verify(this IEnumerable<RudeEditDiagnostic> diagnostics, string newSource, params RudeEditDiagnosticDescription[] expectedDiagnostics)
{
var actualDiagnostics = diagnostics.ToDescription(newSource, expectedDiagnostics.Any(d => d.FirstLine != null)).ToArray();
AssertEx.Equal(expectedDiagnostics, actualDiagnostics, itemSeparator: ",\r\n");
AssertEx.SetEqual(expectedDiagnostics, actualDiagnostics, itemSeparator: ",\r\n");
}
private static IEnumerable<RudeEditDiagnosticDescription> ToDescription(this IEnumerable<RudeEditDiagnostic> diagnostics, string newSource, bool includeFirstLines)
......
......@@ -372,8 +372,8 @@
<ItemGroup>
<EmbeddedResource Include="CSharpFeaturesResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CSharpFeaturesResources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
<LastGenOutput>CSharpFeaturesResources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
......@@ -386,4 +386,4 @@
<Import Project="..\..\..\build\VSL.Imports.Closed.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -60,6 +60,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to anonymous method.
/// </summary>
internal static string AnonymousMethod {
get {
return ResourceManager.GetString("AnonymousMethod", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Autoselect disabled due to possible explicitly named anonymous type member creation..
/// </summary>
......@@ -123,6 +132,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to await expression.
/// </summary>
internal static string AwaitExpression {
get {
return ResourceManager.GetString("AwaitExpression", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to can&apos;t determine valid range of statements to extract out.
/// </summary>
......@@ -141,6 +159,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to catch clause.
/// </summary>
internal static string CatchClause {
get {
return ResourceManager.GetString("CatchClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Change return type from {0} to {1}.
/// </summary>
......@@ -159,6 +186,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to checked statement.
/// </summary>
internal static string CheckedStatement {
get {
return ResourceManager.GetString("CheckedStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Conflict(s) detected..
/// </summary>
......@@ -213,6 +249,42 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to filter clause.
/// </summary>
internal static string FilterClause {
get {
return ResourceManager.GetString("FilterClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to finally clause.
/// </summary>
internal static string FinallyClause {
get {
return ResourceManager.GetString("FinallyClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to fixed statement.
/// </summary>
internal static string FixedStatement {
get {
return ResourceManager.GetString("FixedStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to foreach statement.
/// </summary>
internal static string ForEachStatement {
get {
return ResourceManager.GetString("ForEachStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to TODO: free unmanaged resources (unmanaged objects) and override a finalizer below..
/// </summary>
......@@ -222,6 +294,24 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to from clause.
/// </summary>
internal static string FromClause {
get {
return ResourceManager.GetString("FromClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to groupby clause.
/// </summary>
internal static string GroupByClause {
get {
return ResourceManager.GetString("GroupByClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Implement Abstract Class.
/// </summary>
......@@ -267,6 +357,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to into clause.
/// </summary>
internal static string IntoClause {
get {
return ResourceManager.GetString("IntoClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid selection..
/// </summary>
......@@ -285,6 +384,24 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to join clause.
/// </summary>
internal static string JoinClause {
get {
return ResourceManager.GetString("JoinClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to lambda.
/// </summary>
internal static string Lambda {
get {
return ResourceManager.GetString("Lambda", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;lambda expression&gt;.
/// </summary>
......@@ -294,6 +411,24 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to let clause.
/// </summary>
internal static string LetClause {
get {
return ResourceManager.GetString("LetClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to lock statement.
/// </summary>
internal static string LockStatement {
get {
return ResourceManager.GetString("LockStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make the containing scope &apos;async&apos;..
/// </summary>
......@@ -366,6 +501,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to orderby clause.
/// </summary>
internal static string OrderByClause {
get {
return ResourceManager.GetString("OrderByClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Organize Usings.
/// </summary>
......@@ -393,6 +537,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to query body.
/// </summary>
internal static string QueryBody {
get {
return ResourceManager.GetString("QueryBody", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &lt;range variable&gt;.
/// </summary>
......@@ -447,6 +600,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to select clause.
/// </summary>
internal static string SelectClause {
get {
return ResourceManager.GetString("SelectClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Selection can not be part of constant initializer expression..
/// </summary>
......@@ -591,6 +753,24 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to try block.
/// </summary>
internal static string TryBlock {
get {
return ResourceManager.GetString("TryBlock", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to unchecked statement.
/// </summary>
internal static string UncheckedStatement {
get {
return ResourceManager.GetString("UncheckedStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to TODO: uncomment the following line if the finalizer is overridden above..
/// </summary>
......@@ -600,6 +780,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to using statement.
/// </summary>
internal static string UsingStatement {
get {
return ResourceManager.GetString("UsingStatement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Warning: Declaration changes scope and may change meaning..
/// </summary>
......@@ -608,5 +797,23 @@ internal class CSharpFeaturesResources {
return ResourceManager.GetString("WarningDeclarationChangesScope", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to where clause.
/// </summary>
internal static string WhereClause {
get {
return ResourceManager.GetString("WhereClause", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to yield statement.
/// </summary>
internal static string YieldStatement {
get {
return ResourceManager.GetString("YieldStatement", resourceCulture);
}
}
}
}
......@@ -300,4 +300,73 @@
<data name="UncommentTheFollowingIfFinalizerOverriddenTodo" xml:space="preserve">
<value>TODO: uncomment the following line if the finalizer is overridden above.</value>
</data>
<data name="TryBlock" xml:space="preserve">
<value>try block</value>
</data>
<data name="CatchClause" xml:space="preserve">
<value>catch clause</value>
</data>
<data name="FilterClause" xml:space="preserve">
<value>filter clause</value>
</data>
<data name="FinallyClause" xml:space="preserve">
<value>finally clause</value>
</data>
<data name="FixedStatement" xml:space="preserve">
<value>fixed statement</value>
</data>
<data name="UsingStatement" xml:space="preserve">
<value>using statement</value>
</data>
<data name="LockStatement" xml:space="preserve">
<value>lock statement</value>
</data>
<data name="ForEachStatement" xml:space="preserve">
<value>foreach statement</value>
</data>
<data name="CheckedStatement" xml:space="preserve">
<value>checked statement</value>
</data>
<data name="UncheckedStatement" xml:space="preserve">
<value>unchecked statement</value>
</data>
<data name="YieldStatement" xml:space="preserve">
<value>yield statement</value>
</data>
<data name="AwaitExpression" xml:space="preserve">
<value>await expression</value>
</data>
<data name="Lambda" xml:space="preserve">
<value>lambda</value>
</data>
<data name="AnonymousMethod" xml:space="preserve">
<value>anonymous method</value>
</data>
<data name="FromClause" xml:space="preserve">
<value>from clause</value>
</data>
<data name="JoinClause" xml:space="preserve">
<value>join clause</value>
</data>
<data name="LetClause" xml:space="preserve">
<value>let clause</value>
</data>
<data name="WhereClause" xml:space="preserve">
<value>where clause</value>
</data>
<data name="OrderByClause" xml:space="preserve">
<value>orderby clause</value>
</data>
<data name="SelectClause" xml:space="preserve">
<value>select clause</value>
</data>
<data name="GroupByClause" xml:space="preserve">
<value>groupby clause</value>
</data>
<data name="QueryBody" xml:space="preserve">
<value>query body</value>
</data>
<data name="IntoClause" xml:space="preserve">
<value>into clause</value>
</data>
</root>
\ No newline at end of file
......@@ -1145,18 +1145,31 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node,
case SyntaxKind.QueryExpression:
return ((QueryExpressionSyntax)node).FromClause.FromKeyword.Span;
case SyntaxKind.QueryBody:
var queryBody = (QueryBodySyntax)node;
return GetDiagnosticSpanImpl(queryBody.Clauses.FirstOrDefault() ?? queryBody.Parent, editKind);
case SyntaxKind.QueryContinuation:
return ((QueryContinuationSyntax)node).IntoKeyword.Span;
case SyntaxKind.FromClause:
return ((FromClauseSyntax)node).FromKeyword.Span;
case SyntaxKind.JoinClause:
return ((JoinClauseSyntax)node).JoinKeyword.Span;
case SyntaxKind.JoinIntoClause:
return ((JoinIntoClauseSyntax)node).IntoKeyword.Span;
case SyntaxKind.LetClause:
return ((LetClauseSyntax)node).LetKeyword.Span;
case SyntaxKind.WhereClause:
return ((WhereClauseSyntax)node).WhereKeyword.Span;
case SyntaxKind.OrderByClause:
return ((OrderByClauseSyntax)node).OrderByKeyword.Span;
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
return node.Span;
......@@ -1168,7 +1181,7 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node,
return ((GroupClauseSyntax)node).GroupKeyword.Span;
default:
throw ExceptionUtilities.Unreachable;
throw ExceptionUtilities.UnexpectedValue(kind);
}
}
......@@ -1316,73 +1329,81 @@ internal static string GetStatementDisplayNameImpl(SyntaxNode node)
switch (node.Kind())
{
case SyntaxKind.TryStatement:
return "try block";
return CSharpFeaturesResources.TryBlock;
case SyntaxKind.CatchClause:
return "catch clause";
return CSharpFeaturesResources.CatchClause;
case SyntaxKind.CatchFilterClause:
return "filter clause";
return CSharpFeaturesResources.FilterClause;
case SyntaxKind.FinallyClause:
return "finally clause";
return CSharpFeaturesResources.FinallyClause;
case SyntaxKind.FixedStatement:
return "fixed statement";
return CSharpFeaturesResources.FixedStatement;
case SyntaxKind.UsingStatement:
return "using statement";
return CSharpFeaturesResources.UsingStatement;
case SyntaxKind.LockStatement:
return "lock statement";
return CSharpFeaturesResources.LockStatement;
case SyntaxKind.ForEachStatement:
return "foreach statement";
return CSharpFeaturesResources.ForEachStatement;
case SyntaxKind.CheckedStatement:
return "checked statement";
return CSharpFeaturesResources.CheckedStatement;
case SyntaxKind.UncheckedStatement:
return "unchecked statement";
return CSharpFeaturesResources.UncheckedStatement;
case SyntaxKind.YieldBreakStatement:
case SyntaxKind.YieldReturnStatement:
return "yield statement";
return CSharpFeaturesResources.YieldStatement;
case SyntaxKind.AwaitExpression:
return "await expression";
return CSharpFeaturesResources.AwaitExpression;
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
return "lambda";
return CSharpFeaturesResources.Lambda;
case SyntaxKind.AnonymousMethodExpression:
return "anonymous method";
return CSharpFeaturesResources.AnonymousMethod;
case SyntaxKind.FromClause:
return "from clause";
return CSharpFeaturesResources.FromClause;
case SyntaxKind.JoinClause:
return "join clause";
case SyntaxKind.JoinIntoClause:
return CSharpFeaturesResources.JoinClause;
case SyntaxKind.LetClause:
return "let clause";
return CSharpFeaturesResources.LetClause;
case SyntaxKind.WhereClause:
return "where clause";
return CSharpFeaturesResources.WhereClause;
case SyntaxKind.OrderByClause:
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
return "orderby clause";
return CSharpFeaturesResources.OrderByClause;
case SyntaxKind.SelectClause:
return "select clause";
return CSharpFeaturesResources.SelectClause;
case SyntaxKind.GroupClause:
return "groupby clause";
return CSharpFeaturesResources.GroupByClause;
case SyntaxKind.QueryBody:
return CSharpFeaturesResources.QueryBody;
case SyntaxKind.QueryContinuation:
return CSharpFeaturesResources.IntoClause;
default:
throw ExceptionUtilities.Unreachable;
throw ExceptionUtilities.UnexpectedValue(node.Kind());
}
}
......
......@@ -51,28 +51,29 @@ protected internal override IEnumerable<SyntaxNode> GetChildren(SyntaxNode node)
return EnumerateRootChildren(node);
}
return NonRootHasChildren(node) ? EnumerateChildren(node) : null;
return IsLeaf(node) ? null : EnumerateNonRootChildren(node);
}
private IEnumerable<SyntaxNode> EnumerateChildren(SyntaxNode node)
private IEnumerable<SyntaxNode> EnumerateNonRootChildren(SyntaxNode node)
{
foreach (var child in node.ChildNodesAndTokens())
foreach (var child in node.ChildNodes())
{
var childNode = child.AsNode();
if (childNode != null)
if (SyntaxFacts.IsLambdaBody(child))
{
if (GetLabel(childNode) != IgnoredNode)
{
yield return childNode;
}
else
continue;
}
if (GetLabelImpl(child) != Label.Ignored)
{
yield return child;
}
else
{
foreach (var descendant in child.DescendantNodes(descendIntoChildren: DescendIntoChildren))
{
foreach (var descendant in childNode.DescendantNodes(descendIntoChildren: SyntaxUtilities.IsNotLambda))
if (HasLabel(descendant))
{
if (EnumerateExpressionDescendant(descendant))
{
yield return descendant;
}
yield return descendant;
}
}
}
......@@ -81,17 +82,17 @@ private IEnumerable<SyntaxNode> EnumerateChildren(SyntaxNode node)
private IEnumerable<SyntaxNode> EnumerateRootChildren(SyntaxNode root)
{
var childNode = (root == _oldRoot) ? _oldRootChild : _newRootChild;
var child = (root == _oldRoot) ? _oldRootChild : _newRootChild;
if (GetLabel(childNode) != IgnoredNode)
if (GetLabelImpl(child) != Label.Ignored)
{
yield return childNode;
yield return child;
}
else
{
foreach (var descendant in childNode.DescendantNodes(descendIntoChildren: SyntaxUtilities.IsNotLambda))
foreach (var descendant in child.DescendantNodes(descendIntoChildren: DescendIntoChildren))
{
if (EnumerateExpressionDescendant(descendant))
if (HasLabel(descendant))
{
yield return descendant;
}
......@@ -99,46 +100,39 @@ private IEnumerable<SyntaxNode> EnumerateRootChildren(SyntaxNode root)
}
}
private static bool EnumerateExpressionDescendant(SyntaxNode node)
private bool DescendIntoChildren(SyntaxNode node)
{
return
node.IsKind(SyntaxKind.VariableDeclarator) ||
node.IsKind(SyntaxKind.AwaitExpression) ||
SyntaxUtilities.IsLambda(node);
return !SyntaxFacts.IsLambdaBody(node) && !HasLabel(node);
}
protected internal sealed override IEnumerable<SyntaxNode> GetDescendants(SyntaxNode node)
{
if (node == _oldRoot || node == _newRoot)
{
var descendantNode = (node == _oldRoot) ? _oldRootChild : _newRootChild;
var descendant = (node == _oldRoot) ? _oldRootChild : _newRootChild;
if (GetLabel(descendantNode) != IgnoredNode)
if (GetLabelImpl(descendant) != Label.Ignored)
{
yield return descendantNode;
yield return descendant;
}
node = descendantNode;
node = descendant;
}
foreach (var descendant in node.DescendantNodes(
descendIntoChildren: NonRootHasChildren,
descendIntoTrivia: false))
foreach (var descendant in node.DescendantNodes(descendIntoChildren: c => c == node || !IsLeaf(c) && !SyntaxFacts.IsLambdaBody(c)))
{
if (GetLabel(descendant) != IgnoredNode)
if (!SyntaxFacts.IsLambdaBody(descendant) && HasLabel(descendant))
{
yield return descendant;
}
}
}
private static bool NonRootHasChildren(SyntaxNode node)
private static bool IsLeaf(SyntaxNode node)
{
// Leaves are labeled statements that don't have a labeled child.
// A non-labeled statement may not be leave since it may contain a lambda.
bool isLeaf;
Classify(node.Kind(), node, out isLeaf);
return !isLeaf;
return isLeaf;
}
#endregion
......@@ -194,13 +188,18 @@ internal enum Label
AwaitExpression,
Lambda,
FromClauseLambda,
LetClauseLambda,
WhereClauseLambda,
OrderingLambda,
SelectClauseLambda,
JoinClauseLambda,
GroupClauseLambda,
FromClause,
QueryBody,
FromClauseLambda, // tied to parent
LetClauseLambda, // tied to parent
WhereClauseLambda, // tied to parent
OrderByClause, // tied to parent
OrderingLambda, // tied to parent
SelectClauseLambda, // tied to parent
JoinClauseLambda, // tied to parent
JoinIntoClause, // tied to parent
GroupClauseLambda, // tied to parent
QueryContinuation, // tied to parent
// helpers:
Count,
......@@ -222,6 +221,16 @@ private static int TiedToAncestor(Label label)
case Label.FinallyClause:
case Label.ForStatementPart:
case Label.YieldStatement:
case Label.FromClauseLambda:
case Label.LetClauseLambda:
case Label.WhereClauseLambda:
case Label.OrderByClause:
case Label.OrderingLambda:
case Label.SelectClauseLambda:
case Label.JoinClauseLambda:
case Label.JoinIntoClause:
case Label.GroupClauseLambda:
case Label.QueryContinuation:
return 1;
default:
......@@ -360,14 +369,16 @@ internal static Label Classify(SyntaxKind kind, SyntaxNode nodeOpt, out bool isL
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
isLeaf = true;
return Label.Lambda;
case SyntaxKind.FromClause:
// The first from clause of a query is not a lambda.
// We have to assign it a label different from "FromClauseLambda"
// so that we won't match lambda-from to non-lambda-from.
// Since such from clause is just another expression the label should be "Ignored".
//
// Since FromClause declares range variables we need to include it in the map,
// so that we are able to map range variable declarations.
// Therefore we assign it a dedicated label.
//
// The parent is not available only when comparing nodes for value equality.
// In that case we use "Ignored" for all from clauses, even when they translate to lambdas.
......@@ -375,36 +386,40 @@ internal static Label Classify(SyntaxKind kind, SyntaxNode nodeOpt, out bool isL
// in the from clause lambda, which is ok.
if (nodeOpt == null || nodeOpt.Parent.IsKind(SyntaxKind.QueryExpression))
{
// may contain lambda, so it's not a leaf node
return Label.Ignored;
return Label.FromClause;
}
isLeaf = true;
return Label.FromClauseLambda;
case SyntaxKind.QueryBody:
return Label.QueryBody;
case SyntaxKind.QueryContinuation:
return Label.QueryContinuation;
case SyntaxKind.LetClause:
isLeaf = true;
return Label.LetClauseLambda;
case SyntaxKind.WhereClause:
isLeaf = true;
return Label.WhereClauseLambda;
case SyntaxKind.OrderByClause:
return Label.OrderByClause;
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
isLeaf = true;
return Label.OrderingLambda;
case SyntaxKind.SelectClause:
isLeaf = true;
return Label.SelectClauseLambda;
case SyntaxKind.JoinClause:
isLeaf = true;
return Label.JoinClauseLambda;
case SyntaxKind.JoinIntoClause:
return Label.JoinIntoClause;
case SyntaxKind.GroupClause:
isLeaf = true;
return Label.GroupClauseLambda;
case SyntaxKind.IdentifierName:
......@@ -420,7 +435,6 @@ internal static Label Classify(SyntaxKind kind, SyntaxNode nodeOpt, out bool isL
case SyntaxKind.OmittedTypeArgument:
case SyntaxKind.NameColon:
case SyntaxKind.StackAllocArrayCreationExpression:
case SyntaxKind.JoinIntoClause:
case SyntaxKind.OmittedArraySizeExpression:
case SyntaxKind.ThisExpression:
case SyntaxKind.BaseExpression:
......@@ -434,7 +448,7 @@ internal static Label Classify(SyntaxKind kind, SyntaxNode nodeOpt, out bool isL
case SyntaxKind.TypeOfExpression:
case SyntaxKind.SizeOfExpression:
case SyntaxKind.DefaultExpression:
// can't contain a lambda:
// can't contain a lambda/await/anonymous type:
isLeaf = true;
return Label.Ignored;
......@@ -506,18 +520,16 @@ public override bool ValuesEqual(SyntaxNode left, SyntaxNode right)
return true;
default:
// When a value of a statement containing lambdas, and await expressions, e.g.
// F(x => x, G(y => y), from x in y select x, await a);
// is compared we descend into expressions and include them in the value comparison (they are not labeled)
// but not into the contained lambda and await expression nodes (because they are labeled).
//
if (NonRootHasChildren(left))
// When comparing the value of a node with its partner we are deciding whether to add an Update edit for the pair.
// If the actual change is under a descendant labeled node we don't want to attribute it to the node being compared,
// so we skip all labeled children when recursively checking for equivalence.
if (IsLeaf(left))
{
ignoreChildNode = IgnoreLabeledChild;
ignoreChildNode = null;
}
else
{
ignoreChildNode = null;
ignoreChildNode = IgnoreLabeledChild;
}
break;
......
......@@ -186,6 +186,21 @@ public static bool IsLambda(SyntaxNode node)
return false;
}
public static bool IsRangeVariableDeclarator(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.FromClause:
case SyntaxKind.JoinClause:
case SyntaxKind.LetClause:
case SyntaxKind.JoinIntoClause:
case SyntaxKind.QueryContinuation:
return true;
}
return false;
}
public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2)
{
body1 = null;
......@@ -200,15 +215,13 @@ public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out
return true;
case SyntaxKind.FromClause:
var fromClause = (FromClauseSyntax)node;
// The first from clause of a query expression is not a lambda.
if (fromClause.Parent.IsKind(SyntaxKind.QueryExpression))
if (node.Parent.IsKind(SyntaxKind.QueryExpression))
{
return false;
}
body1 = fromClause.Expression;
body1 = ((FromClauseSyntax)node).Expression;
return true;
case SyntaxKind.JoinClause:
......
......@@ -2985,26 +2985,41 @@ private SyntaxNode GetVariableSyntax(ISymbol localOrParameter, CancellationToken
ISymbol oldCapture = oldCaptures[oldCaptureIndex];
// Parameter capture can't be changed to local capture and vice versa
// because parameters can't be introduced or deleted during EnC
// (we checked above for changes in lambda signatures).
// Also range variables can't be mapped to other variables since htey have
// different kinds of delcarator syntax nodes.
Debug.Assert(oldCapture.Kind == newCapture.Kind);
// Range variables don't have types. Each transparent identifier (range variable use)
// might have a different type. Changing these types is ok as long as the containing lambda
// signatures remain unchanged, which we validate for all lambdas in general.
//
// The scope of a transparent identifier is the containing lambda body. Since we verify that
// each lambda body accesses the same captured variables (including range variables)
// the corresponding scopes are guaranteed to be preserved as well.
if (oldCapture.Kind == SymbolKind.RangeVariable)
{
continue;
}
// type check
var oldType = GetType(oldCapture);
var newType = GetType(newCapture);
if (!s_assemblyEqualityComparer.Equals(oldType, newType))
var oldTypeOpt = GetType(oldCapture);
var newTypeOpt = GetType(newCapture);
if (!s_assemblyEqualityComparer.Equals(oldTypeOpt, newTypeOpt))
{
diagnostics.Add(new RudeEditDiagnostic(
RudeEditKind.ChangingCapturedVariableType,
newSyntaxOpt.Span,
null,
new[] { newCapture.Name, oldType.ToDisplayString(ErrorDisplayFormat) }));
new[] { newCapture.Name, oldTypeOpt.ToDisplayString(ErrorDisplayFormat) }));
hasErrors = true;
continue;
}
// Parameter capture can't be changed to local capture and vice versa
// because parameters can't be introduced or deleted during EnC
// (we checked above for changes in lambda signatures).
Debug.Assert(oldCapture.Kind == newCapture.Kind);
// scope check:
SyntaxNode oldScopeOpt = GetCapturedVariableScope(oldCapture, oldMemberBody, cancellationToken);
SyntaxNode newScopeOpt = GetCapturedVariableScope(newCapture, newMemberBody, cancellationToken);
......@@ -3108,11 +3123,23 @@ private void ReportLambdaSignatureRudeEdits(SemanticModel oldModel, SyntaxNode o
private static ITypeSymbol GetType(ISymbol localOrParameter)
{
return (localOrParameter.Kind == SymbolKind.Parameter) ? ((IParameterSymbol)localOrParameter).Type : ((ILocalSymbol)localOrParameter).Type;
switch (localOrParameter.Kind)
{
case SymbolKind.Parameter:
return ((IParameterSymbol)localOrParameter).Type;
case SymbolKind.Local:
return ((ILocalSymbol)localOrParameter).Type;
default:
throw ExceptionUtilities.UnexpectedValue(localOrParameter.Kind);
}
}
private SyntaxNode GetCapturedVariableScope(ISymbol localOrParameter, SyntaxNode memberBody, CancellationToken cancellationToken)
{
Debug.Assert(localOrParameter.Kind != SymbolKind.RangeVariable);
if (localOrParameter.Kind == SymbolKind.Parameter)
{
var member = localOrParameter.ContainingSymbol;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册