diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs index 0e059e738013a9cdd9dd9f3a21d99dbd5552d5f9..f720c444b6f42805813bc44c45d0f91b232a75cb 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs @@ -47,9 +47,9 @@ public BoundLambda(CSharpSyntaxNode syntax, BoundBlock body, ImmutableArray GetDiagnostics() return this.SyntaxTree.GetDiagnostics(this); } - internal sealed override SyntaxNode GetCorrespondingLambdaBody(SyntaxNode body) + internal sealed override SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode body) { - return LambdaUtilities.GetCorrespondingLambdaBody(body, this); + return LambdaUtilities.TryGetCorrespondingLambdaBody(body, this); } internal override SyntaxNode GetLambda() diff --git a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs index c1f25567c967b144b1a668ecb0df0438fa76152d..d457ac9a0cc2e7803b354c1d94ab13b79bcf67a8 100644 --- a/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs +++ b/src/Compilers/CSharp/Portable/Syntax/LambdaUtilities.cs @@ -22,11 +22,14 @@ public static bool IsLambda(SyntaxNode node) case SyntaxKind.WhereClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: - case SyntaxKind.SelectClause: case SyntaxKind.JoinClause: case SyntaxKind.GroupClause: return true; + case SyntaxKind.SelectClause: + var selectClause = (SelectClauseSyntax)node; + return !IsReducedSelectOrGroupByClause(selectClause, selectClause.Expression); + case SyntaxKind.FromClause: // The first from clause of a query expression is not a lambda. return !node.Parent.IsKind(SyntaxKind.QueryExpression); @@ -50,7 +53,7 @@ public static SyntaxNode GetLambda(SyntaxNode lambdaBody) /// /// See SyntaxNode.GetCorrespondingLambdaBody. /// - internal static SyntaxNode GetCorrespondingLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) + internal static SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) { var oldLambda = oldBody.Parent; switch (oldLambda.Kind()) @@ -74,7 +77,11 @@ internal static SyntaxNode GetCorrespondingLambdaBody(SyntaxNode oldBody, Syntax return ((OrderingSyntax)newLambda).Expression; case SyntaxKind.SelectClause: - return ((SelectClauseSyntax)newLambda).Expression; + var selectClause = (SelectClauseSyntax)newLambda; + + // Select clause is not considered to be lambda if it's reduced, + // however to avoid complexity we allow it to be passed in and just return null. + return IsReducedSelectOrGroupByClause(selectClause, selectClause.Expression) ? null : selectClause.Expression; case SyntaxKind.JoinClause: var oldJoin = (JoinClauseSyntax)oldLambda; @@ -86,7 +93,8 @@ internal static SyntaxNode GetCorrespondingLambdaBody(SyntaxNode oldBody, Syntax var oldGroup = (GroupClauseSyntax)oldLambda; var newGroup = (GroupClauseSyntax)newLambda; Debug.Assert(oldGroup.GroupExpression == oldBody || oldGroup.ByExpression == oldBody); - return (oldGroup.GroupExpression == oldBody) ? newGroup.GroupExpression : newGroup.ByExpression; + return (oldGroup.GroupExpression == oldBody) ? + (IsReducedSelectOrGroupByClause(newGroup, newGroup.GroupExpression) ? null : newGroup.GroupExpression) : newGroup.ByExpression; default: throw ExceptionUtilities.UnexpectedValue(oldLambda.Kind()); @@ -101,7 +109,7 @@ public static bool IsNotLambdaBody(SyntaxNode node) /// /// Returns true if the specified represents a body of a lambda. /// - public static bool IsLambdaBody(SyntaxNode node) + public static bool IsLambdaBody(SyntaxNode node, bool allowReducedLambas = false) { var parent = node?.Parent; if (parent == null) @@ -140,16 +148,83 @@ public static bool IsLambdaBody(SyntaxNode node) case SyntaxKind.SelectClause: var selectClause = (SelectClauseSyntax)parent; - return selectClause.Expression == node; + return selectClause.Expression == node && (allowReducedLambas || !IsReducedSelectOrGroupByClause(selectClause, selectClause.Expression)); case SyntaxKind.GroupClause: var groupClause = (GroupClauseSyntax)parent; - return groupClause.GroupExpression == node || groupClause.ByExpression == node; + return (groupClause.GroupExpression == node && (allowReducedLambas || !IsReducedSelectOrGroupByClause(groupClause, groupClause.GroupExpression))) || + groupClause.ByExpression == node; } return false; } + /// + /// When queries are translated into expressions select and group-by expressions such that + /// 1) select/group-by expression is the same identifier as the "source" identifier and + /// 2) at least one Where or OrderBy clause but no other clause is present in the contained query body or + /// the expression in question is a group-by expression and the body has no clause + /// + /// do not translate into lambdas. + /// By "source" identifier we mean the identifier specified in the from clause that initiates the query or the query continuation that includes the body. + /// + /// The above condition can be dervied from the language specification (chapter 7.16.2) as follows: + /// - In order for 7.16.2.5 "Select clauses" to be applicable the following conditions must hold: + /// - There has to be at least one clause in the body, otherwise the query is reduced into a final form by 7.16.2.3 "Degenerate query expressions". + /// - Only where and order-by clauses may be present in the query body, otherwise a transformation in 7.16.2.4 "From, let, where, join and orderby clauses" + /// produces pattern that doesn't match the requirements of 7.16.2.5. + /// + /// - In order for 7.16.2.6 "Groupby clauses" to be applicable the following conditions must hold: + /// - Only where and order-by clauses may be present in the query body, otherwise a transformation in 7.16.2.4 "From, let, where, join and orderby clauses" + /// produces pattern that doesn't match the requirements of 7.16.2.5. + /// + private static bool IsReducedSelectOrGroupByClause(SelectOrGroupClauseSyntax selectOrGroupClause, ExpressionSyntax selectOrGroupExpression) + { + if (!selectOrGroupExpression.IsKind(SyntaxKind.IdentifierName)) + { + return false; + } + + var selectorIdentifier = ((IdentifierNameSyntax)selectOrGroupExpression).Identifier; + + SyntaxToken sourceIdentifier; + QueryBodySyntax containingBody; + + var containingQueryOrContinuation = selectOrGroupClause.Parent.Parent; + if (containingQueryOrContinuation.IsKind(SyntaxKind.QueryExpression)) + { + var containingQuery = (QueryExpressionSyntax)containingQueryOrContinuation; + containingBody = containingQuery.Body; + sourceIdentifier = containingQuery.FromClause.Identifier; + } + else + { + var containingContinuation = (QueryContinuationSyntax)containingQueryOrContinuation; + sourceIdentifier = containingContinuation.Identifier; + containingBody = containingContinuation.Body; + } + + if (!SyntaxFactory.AreEquivalent(sourceIdentifier, selectorIdentifier)) + { + return false; + } + + if (selectOrGroupClause.IsKind(SyntaxKind.SelectClause) && containingBody.Clauses.Count == 0) + { + return false; + } + + foreach (var clause in containingBody.Clauses) + { + if (!clause.IsKind(SyntaxKind.WhereClause) && !clause.IsKind(SyntaxKind.OrderByClause)) + { + return false; + } + } + + return true; + } + /// /// In C# lambda bodies are expressions or block statements. In both cases it's a single node. /// In VB a lambda body might be a sequence of nodes (statements). @@ -212,13 +287,27 @@ public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode lambdaBody return true; case SyntaxKind.SelectClause: - lambdaBody1 = ((SelectClauseSyntax)node).Expression; + var selectClause = (SelectClauseSyntax)node; + if (IsReducedSelectOrGroupByClause(selectClause, selectClause.Expression)) + { + return false; + } + + lambdaBody1 = selectClause.Expression; return true; case SyntaxKind.GroupClause: var groupClause = (GroupClauseSyntax)node; - lambdaBody1 = groupClause.GroupExpression; - lambdaBody2 = groupClause.ByExpression; + if (IsReducedSelectOrGroupByClause(groupClause, groupClause.GroupExpression)) + { + lambdaBody1 = groupClause.ByExpression; + } + else + { + lambdaBody1 = groupClause.GroupExpression; + lambdaBody2 = groupClause.ByExpression; + } + return true; } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs index e33bd3e6c5079099f70989ecb777ab303527ba85..d769e5596bb855f4c50cd52382119e21b1b29b83 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueClosureTests.cs @@ -2042,5 +2042,491 @@ class C "C: {<> c}", "C.<>c: {<>9__2_0#1, <>9__2_0, <>9__2_2#1, <.ctor>b__2_0#1, <.ctor>b__2_0, <.ctor>b__2_2#1}"); } + + [Fact] + public void Queries_Select_Reduced1() + { + var source0 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + where a > 0 + select a; + } + + int[] array = null; +} +"); + var source1 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + where a > 0 + select a + 1; + } + + int[] array = null; +} +"); + var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + 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))); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // new lambda for Select(a => a + 1) + diff1.VerifySynthesizedMembers( + "C: {<>c}", + "C.<>c: {<>9__0_0, <>9__0_1#1, b__0_0, b__0_1#1}"); + + diff1.VerifyIL("C.<>c.b__0_1#1", @" +{ + // Code size 4 (0x4) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldc.i4.1 + IL_0002: add + IL_0003: ret +} +"); + // old query: + v0.VerifyIL("C.F", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""bool C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: stloc.0 + IL_002c: ret +} +"); + // new query: + diff1.VerifyIL("C.F", @" +{ + // Code size 81 (0x51) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""bool C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: ldsfld ""System.Func C.<>c.<>9__0_1#1"" + IL_0030: dup + IL_0031: brtrue.s IL_004a + IL_0033: pop + IL_0034: ldsfld ""C.<>c C.<>c.<>9"" + IL_0039: ldftn ""int C.<>c.b__0_1#1(int)"" + IL_003f: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0044: dup + IL_0045: stsfld ""System.Func C.<>c.<>9__0_1#1"" + IL_004a: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)"" + IL_004f: stloc.0 + IL_0050: ret +} +"); + } + + [Fact] + public void Queries_Select_Reduced2() + { + var source0 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + where a > 0 + select a + 1; + } + + int[] array = null; +} +"); + var source1 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + where a > 0 + select a; + } + + int[] array = null; +} +"); + var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + 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))); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // lambda for Select(a => a + 1) is gone + diff1.VerifySynthesizedMembers( + "C: {<>c}", + "C.<>c: {<>9__0_0, b__0_0}"); + + // old query: + v0.VerifyIL("C.F", @" +{ + // Code size 81 (0x51) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""bool C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: ldsfld ""System.Func C.<>c.<>9__0_1"" + IL_0030: dup + IL_0031: brtrue.s IL_004a + IL_0033: pop + IL_0034: ldsfld ""C.<>c C.<>c.<>9"" + IL_0039: ldftn ""int C.<>c.b__0_1(int)"" + IL_003f: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0044: dup + IL_0045: stsfld ""System.Func C.<>c.<>9__0_1"" + IL_004a: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable, System.Func)"" + IL_004f: stloc.0 + IL_0050: ret +} +"); + // new query: + diff1.VerifyIL("C.F", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""bool C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: stloc.0 + IL_002c: ret +} +"); + } + + [Fact] + public void Queries_GroupBy_Reduced1() + { + var source0 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + group a by a; + } + + int[] array = null; +} +"); + var source1 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + group a + 1 by a; + } + + int[] array = null; +} +"); + var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + 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))); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // new lambda for GroupBy(..., a => a + 1) + diff1.VerifySynthesizedMembers( + "C: {<>c}", + "C.<>c: {<>9__0_0, <>9__0_1#1, b__0_0, b__0_1#1}"); + + diff1.VerifyIL("C.<>c.b__0_1#1", @" +{ + // Code size 4 (0x4) + .maxstack 2 + IL_0000: ldarg.1 + IL_0001: ldc.i4.1 + IL_0002: add + IL_0003: ret +} +"); + // old query: + v0.VerifyIL("C.F", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable> V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""int C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable> System.Linq.Enumerable.GroupBy(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: stloc.0 + IL_002c: ret +} +"); + // new query: + diff1.VerifyIL("C.F", @" +{ + // Code size 76 (0x4c) + .maxstack 4 + .locals init (System.Collections.Generic.IEnumerable> V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""int C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: ldsfld ""System.Func C.<>c.<>9__0_1#1"" + IL_002b: dup + IL_002c: brtrue.s IL_0045 + IL_002e: pop + IL_002f: ldsfld ""C.<>c C.<>c.<>9"" + IL_0034: ldftn ""int C.<>c.b__0_1#1(int)"" + IL_003a: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_003f: dup + IL_0040: stsfld ""System.Func C.<>c.<>9__0_1#1"" + IL_0045: call ""System.Collections.Generic.IEnumerable> System.Linq.Enumerable.GroupBy(System.Collections.Generic.IEnumerable, System.Func, System.Func)"" + IL_004a: stloc.0 + IL_004b: ret +} +"); + } + + [Fact] + public void Queries_GroupBy_Reduced2() + { + var source0 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + group a + 1 by a; + } + + int[] array = null; +} +"); + var source1 = MarkedSource(@" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in array + group a by a; + } + + int[] array = null; +} +"); + var compilation0 = CreateCompilationWithMscorlib(source0.Tree, new[] { SystemCoreRef }, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + 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))); + + var md1 = diff1.GetMetadata(); + var reader1 = md1.Reader; + + // lambda for GroupBy(..., a => a + 1) is gone + diff1.VerifySynthesizedMembers( + "C: {<>c}", + "C.<>c: {<>9__0_0, b__0_0}"); + + // old query: + v0.VerifyIL("C.F", @" +{ + // Code size 76 (0x4c) + .maxstack 4 + .locals init (System.Collections.Generic.IEnumerable> V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""int C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: ldsfld ""System.Func C.<>c.<>9__0_1"" + IL_002b: dup + IL_002c: brtrue.s IL_0045 + IL_002e: pop + IL_002f: ldsfld ""C.<>c C.<>c.<>9"" + IL_0034: ldftn ""int C.<>c.b__0_1(int)"" + IL_003a: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_003f: dup + IL_0040: stsfld ""System.Func C.<>c.<>9__0_1"" + IL_0045: call ""System.Collections.Generic.IEnumerable> System.Linq.Enumerable.GroupBy(System.Collections.Generic.IEnumerable, System.Func, System.Func)"" + IL_004a: stloc.0 + IL_004b: ret +} +"); + // new query: + diff1.VerifyIL("C.F", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + .locals init (System.Collections.Generic.IEnumerable> V_0) //result + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: ldfld ""int[] C.array"" + IL_0007: ldsfld ""System.Func C.<>c.<>9__0_0"" + IL_000c: dup + IL_000d: brtrue.s IL_0026 + IL_000f: pop + IL_0010: ldsfld ""C.<>c C.<>c.<>9"" + IL_0015: ldftn ""int C.<>c.b__0_0(int)"" + IL_001b: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0020: dup + IL_0021: stsfld ""System.Func C.<>c.<>9__0_0"" + IL_0026: call ""System.Collections.Generic.IEnumerable> System.Linq.Enumerable.GroupBy(System.Collections.Generic.IEnumerable, System.Func)"" + IL_002b: stloc.0 + IL_002c: ret +} +"); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Syntax/LambdaUtilitiesTests.cs b/src/Compilers/CSharp/Test/Syntax/Syntax/LambdaUtilitiesTests.cs index 493ffdbc8ef4e4076a65f6befa3bcf4f1370e0a5..881282a55d9adf4454412e0c7de44534eab3467b 100644 --- a/src/Compilers/CSharp/Test/Syntax/Syntax/LambdaUtilitiesTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Syntax/LambdaUtilitiesTests.cs @@ -1,11 +1,189 @@ // 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.Linq; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { - public class LambdaUtilitiesTests + public class LambdaUtilitiesTests : CSharpTestBase { + private void TestLambdaBody(string markedExpression, bool isLambdaBody) + { + string markedSource = @" +using System; +using System.Linq; + +class C +{ + void M() + { + var expr = " + markedExpression + @"; + } + + static T F(T x) => x; +}"; + string source; + int? position; + TextSpan? span; + MarkupTestFile.GetPositionAndSpan(markedSource, out source, out position, out span); + + Assert.Null(position); + Assert.NotNull(span); + + var tree = SyntaxFactory.ParseSyntaxTree(source); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }, new[] { SystemCoreRef }); + compilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify(); + var model = compilation.GetSemanticModel(tree, ignoreAccessibility: false); + + var enclosingMethod = (IMethodSymbol)model.GetEnclosingSymbol(span.Value.Start); + var enclosingSyntax = enclosingMethod.DeclaringSyntaxReferences.Single().GetSyntax(); + bool expected = enclosingMethod.MethodKind == MethodKind.LambdaMethod && enclosingSyntax.Span.Contains(span.Value); + + var node = tree.GetRoot().FindNode(span.Value); + Assert.Equal(expected, LambdaUtilities.IsLambdaBody(node)); + Assert.Equal(isLambdaBody, expected); + } + + [Fact] + public void IsLambdaBody_AnonymousFunction1() + { + TestLambdaBody("new Func(() => [|1|])", isLambdaBody: true); + TestLambdaBody("new Func(x => [|x|])", isLambdaBody: true); + TestLambdaBody("new Func((x) => [|x|])", isLambdaBody: true); + TestLambdaBody("new Func(x => [|{ return x; }|])", isLambdaBody: true); + TestLambdaBody("new Func(delegate [|{ return 1; }|] )", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_From1() + { + TestLambdaBody( + "from x in [|new[] { 1 }|] select x", isLambdaBody: false); + + TestLambdaBody( + "from y in new[] { 1 } from x in [|new[] { 2 }|] select x", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_Join1() + { + TestLambdaBody( + "from y in new[] { 1 } join x in [|new[] { 2 }|] on y equals x select x", isLambdaBody: false); + + TestLambdaBody( + "from y in new[] { 1 } join x in new[] { 2 } on [|y|] equals x select x", isLambdaBody: true); + + TestLambdaBody( + "from y in new[] { 1 } join x in new[] { 2 } on y equals [|x|] select x", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_OrderBy1() + { + TestLambdaBody( + "from x in new[] { 1 } orderby [|x|] ascending select x", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } orderby x descending, [|x|] ascending select x", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_Where1() + { + TestLambdaBody( + "from x in new[] { 1 } where [|x > 0|] select x", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_Let1() + { + TestLambdaBody( + "from x in new[] { 1 } let y = [|0|] select y", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_Select1() + { + TestLambdaBody( + "from x in new[] { 1 } select [|x|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 select [|x|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 select [|@x|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } orderby F(x), F(x) descending select [|x|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } orderby x where x > 0 select [|x|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } select x into y where y > 0 select [|y|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } select x into y orderby y select [|y|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } select [|x|] into y where y > 0 select y", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 select [|x|] into y where y > 0 select y", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 select x into y where y > 0 select [|y|]", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } orderby x let z = x where x > 0 select [|x|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } from y in new[] { 2 } select [|x|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } from y in new[] { 2 } select [|y|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } join y in new[] { 2 } on x equals y select [|x|]", isLambdaBody: true); + } + + [Fact] + public void IsLambdaBody_GroupBy1() + { + TestLambdaBody( + "from x in new[] { 1 } group [|x|] by x", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } group x by [|x|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 group [|x|] by x", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } where x > 0 group x by [|x|]", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } let y = x group [|x|] by x", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } group [|x|] by x + 1 into y group y by y.Key + 2", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } group x by x + 1 into y group [|y|] by y.Key + 2", isLambdaBody: false); + + TestLambdaBody( + "from x in new[] { 1 } from y in new[] { 2 } group [|x|] by x", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } from y in new[] { 2 } group [|y|] by y", isLambdaBody: true); + + TestLambdaBody( + "from x in new[] { 1 } join y in new[] { 2 } on x equals y group [|x|] by x", isLambdaBody: true); + } + [Fact] public void AreEquivalentIgnoringLambdaBodies1() { @@ -49,6 +227,18 @@ public void AreEquivalentIgnoringLambdaBodies1() SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } select a)"), SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } select a + 1)"))); + Assert.False(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } where a > 0 select a)"), + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } where a > 0 select a + 1)"))); + + Assert.False(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } orderby a select a)"), + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } orderby a select a + 1)"))); + + Assert.True(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } let b = 1 select a)"), + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } let b = 1 select a + 1)"))); + Assert.False(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } select a)"), SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } where b > 0 select a)"))); @@ -69,9 +259,13 @@ public void AreEquivalentIgnoringLambdaBodies1() SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } select a)"), SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } join b in new[] { 3, 4 } on a equals b select a)"))); + Assert.False(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a by a into g select g)"), + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a + 1 by a into g select g)"))); + Assert.True(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a by a into g select g)"), - SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a + 1 by a + 2 into g select g)"))); + SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a by a + 1 into g select g)"))); Assert.False(LambdaUtilities.AreEquivalentIgnoringLambdaBodies( SyntaxFactory.ParseExpression("F(from a in new[] { 1, 2 } group a by a into g select g)"), diff --git a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs index 74c0a79317fada0286c764da002531ff066e85b8..a4a3c541e74e8854311b5bdd355ae36d0b0cf371 100644 --- a/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs +++ b/src/Compilers/Core/Portable/Emit/EditAndContinue/EncVariableSlotAllocator.cs @@ -248,7 +248,12 @@ private bool TryGetPreviousLambdaSyntaxOffset(SyntaxNode lambdaOrLambdaBodySynta SyntaxNode previousSyntax; if (isLambdaBody) { - previousSyntax = previousLambdaSyntax.GetCorrespondingLambdaBody(lambdaOrLambdaBodySyntax); + previousSyntax = previousLambdaSyntax.TryGetCorrespondingLambdaBody(lambdaOrLambdaBodySyntax); + if (previousSyntax == null) + { + previousSyntaxOffset = 0; + return false; + } } else { diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index e0801d82e2059c82c154052cefcff8bf175ad911..96c16029fad116d2b972a435e3c695e4c2d14dbf 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -563,7 +563,7 @@ public SyntaxReference GetReference() /// E.g. join clause declares left expression and right expression -- each of these expressions is a lambda body. /// JoinClause1.GetCorrespondingLambdaBody(JoinClause2.RightExpression) returns JoinClause1.RightExpression. /// - internal abstract SyntaxNode GetCorrespondingLambdaBody(SyntaxNode body); + internal abstract SyntaxNode TryGetCorrespondingLambdaBody(SyntaxNode body); internal abstract SyntaxNode GetLambda(); diff --git a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb index d1a937b19b164a76aea0de86e31be57b057fa95d..dcaf3d42c6112bb566906ebcdd4cb2ae3336feba 100644 --- a/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb +++ b/src/Compilers/VisualBasic/Portable/Syntax/VisualBasicSyntaxNode.vb @@ -677,7 +677,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return SyntaxFactory.AreEquivalent(Me, DirectCast(node, VisualBasicSyntaxNode), topLevel) End Function - Friend Overrides Function GetCorrespondingLambdaBody(body As SyntaxNode) As SyntaxNode + Friend Overrides Function TryGetCorrespondingLambdaBody(body As SyntaxNode) As SyntaxNode Return LambdaUtilities.GetCorrespondingLambdaBody(body, Me) End Function diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index ed8fe792f8eac2ec42be62b74d3f5b45137d7a29..c1c658faaef1bf2b74ffadaef3800e566a9f5d1c 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -266,6 +266,7 @@ + @@ -673,4 +674,4 @@ - + \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index ca5929f1e368724c79fe056433101b18c637262e..85635f69c5ced63964451a63f7c36fbd7a8d64da 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -7194,6 +7194,128 @@ static void Main() Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "join", CSharpFeaturesResources.SelectClause)); } + [Fact] + public void Queries_Select_Reduced1() + { + string src1 = @" +class C +{ + static void Main() + { + var q = from a in array + where a > 0 + select a + 1; + } +}"; + string src2 = @" +class C +{ + static void Main() + { + var q = from a in array + where a > 0 + select a; + } +}"; + + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "select", CSharpFeaturesResources.SelectClause )); + } + + [Fact] + public void Queries_Select_Reduced2() + { + string src1 = @" +class C +{ + static int F(IEnumerbale e) => 1; + + static void Main() + { + F(from a in array where a > 0 select a + 1); + } +}"; + string src2 = @" +class C +{ + static int F(IEnumerbale e) => 1; + + static void Main() + { + F(from a in array where a > 0 select a); + } +}"; + + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "F(from a in array where a > 0 select a);")); + } + + [Fact] + public void Queries_GroupBy_Reduced1() + { + string src1 = @" +class C +{ + static void Main() + { + var q = from a in array + group a + 1 by a; + } +}"; + string src2 = @" +class C +{ + static void Main() + { + var q = from a in array + group a by a; + } +}"; + + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "group", CSharpFeaturesResources.GroupByClause)); + } + + [Fact] + public void Queries_GroupBy_Reduced2() + { + string src1 = @" +class C +{ + static int F(IEnumerbale> e) => 1; + + static void Main() + { + F(from a in array group a by a); + } +}"; + string src2 = @" +class C +{ + static int F(IEnumerbale> e) => 1; + + static void Main() + { + F(from a in array group a + 1 by a); + } +}"; + + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "F(from a in array group a + 1 by a);")); + } + #endregion #region State Machines diff --git a/src/VisualStudio/CSharp/Test/Debugging/BreakpointLocationValidatorTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs similarity index 78% rename from src/VisualStudio/CSharp/Test/Debugging/BreakpointLocationValidatorTests.cs rename to src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs index ccf73cb835d490456d2dc0f67d6d6b1417601792..fd4da69517212486ba27be5fc5625248f9929338 100644 --- a/src/VisualStudio/CSharp/Test/Debugging/BreakpointLocationValidatorTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/BreakpointSpansTests.cs @@ -5,16 +5,15 @@ using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.EditAndContinue; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.LanguageServices.CSharp.Debugging; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.VisualStudio.LanguageServices.CSharp.UnitTests.Debugging { [Trait(Traits.Feature, Traits.Features.DebuggingBreakpoints)] - public class BreakpointLocationValidatorTests + public class BreakpointSpansTests { #region Helpers @@ -42,8 +41,7 @@ private void Test(string markup, bool isMissing, bool isLine, ParseOptions optio var tree = SyntaxFactory.ParseSyntaxTree(source, options); TextSpan breakpointSpan; - bool hasBreakpoint = BreakpointGetter.TryGetBreakpointSpan( - tree, position.Value, CancellationToken.None, out breakpointSpan); + bool hasBreakpoint = BreakpointSpans.TryGetBreakpointSpan(tree, position.Value, CancellationToken.None, out breakpointSpan); if (isLine) { @@ -85,7 +83,7 @@ public static IEnumerable GetBreakpointSequence(SyntaxNode root, int p while (position < endPosition) { TextSpan span; - if (root.TryGetClosestBreakpointSpan(position, out span) && span.End > lastSpanEnd) + if (BreakpointSpans.TryGetClosestBreakpointSpan(root, position, out span) && span.End > lastSpanEnd) { position = lastSpanEnd = span.End; yield return span; @@ -251,40 +249,44 @@ IEnumerable Foo() } [Fact] - public void TestSimpleLambdaBody() + public void ForStatementInitializer1a() { TestSpan( @"class C { void Foo() { - Func f = s => [|F$$oo()|]; +$$ for ([|i = 0|], j = 0; i < 10 && j < 10; i++, j++) + { + } } }"); } [Fact] - public void TestParenthesizedLambdaBody() + public void ForStatementInitializer1b() { TestSpan( @"class C { void Foo() { - Func f = (s, i) => [|F$$oo()|]; + f$$or ([|i = 0|], j = 0; i < 10 && j < 10; i++, j++) + { + } } }"); } [Fact] - public void TestForStatementInitializer1a() + public void ForStatementInitializer1c() { TestSpan( @"class C { void Foo() { -$$ for ([|i = 0|], j = 0; i < 10 && j < 10; i++, j++) + for ([|i $$= 0|], j = 0; i < 10 && j < 10; i++, j++) { } } @@ -292,14 +294,16 @@ void Foo() } [Fact] - public void TestForStatementInitializer1b() + public void ForStatementInitializer1d() { TestSpan( @"class C { void Foo() { - f$$or ([|i = 0|], j = 0; i < 10 && j < 10; i++, j++) + for +$$ ( +[|i = 0|], j = 0; i < 10 && j < 10; i++, j++) { } } @@ -307,14 +311,14 @@ void Foo() } [Fact] - public void TestForStatementInitializer1c() + public void ForStatementInitializer2() { TestSpan( @"class C { void Foo() { - for ([|i $$= 0|], j = 0; i < 10 && j < 10; i++, j++) + for (i = 0, [|$$j = 0|]; i < 10 && j < 10; i++, j++) { } } @@ -322,16 +326,20 @@ void Foo() } [Fact] - public void TestForStatementInitializer1d() + public void ForStatementInitializer3() + { + TestSpan("class C { void M() { for([|i = 0$$|]; ; }; }"); + } + + [Fact] + public void ForStatementCondition() { TestSpan( @"class C { void Foo() { - for -$$ ( -[|i = 0|], j = 0; i < 10 && j < 10; i++, j++) + for (i = 0, j = 0; [|i < 10 && j < $$10|]; i++, j++) { } } @@ -339,14 +347,14 @@ void Foo() } [Fact] - public void TestForStatementInitializer2() + public void ForStatementIncrementor1() { TestSpan( @"class C { void Foo() { - for (i = 0, [|$$j = 0|]; i < 10 && j < 10; i++, j++) + for (i = 0, j = 0; i < 10 && j < 10; [|i+$$+|], j++) { } } @@ -354,73 +362,95 @@ void Foo() } [Fact] - public void TestForStatementInitializer3() + public void ForStatementIncrementor2() { - TestSpan("class C { void M() { for([|i = 0$$|]; ; }; }"); + TestSpan( +@"class C +{ + void Foo() + { + for (i = 0, j = 0; i < 10 && j < 10; i++, [|$$j++|]) + { + } + } +}"); } [Fact] - public void TestForStatementCondition() + public void ForEachStatementExpression() { TestSpan( @"class C { void Foo() { - for (i = 0, j = 0; [|i < 10 && j < $$10|]; i++, j++) + foreach (var v in [|Foo().B$$ar()|]) { } } }"); } + #region Lambdas + [Fact] - public void TestForStatementIncrementor1() + public void SimpleLambdaBody() { TestSpan( @"class C { void Foo() { - for (i = 0, j = 0; i < 10 && j < 10; [|i+$$+|], j++) - { - } + Func f = s => [|F$$oo()|]; } }"); } [Fact] - public void TestForStatementIncrementor2() + public void ParenthesizedLambdaBody() { TestSpan( @"class C { void Foo() { - for (i = 0, j = 0; i < 10 && j < 10; i++, [|$$j++|]) - { - } + Func f = (s, i) => [|F$$oo()|]; } }"); } [Fact] - public void TestForEachStatementExpression() + public void AnonymousMethod1() { TestSpan( @"class C { void Foo() { - foreach (var v in [|Foo().B$$ar()|]) - { - } + Func f = delegate [|$${|] return 1; }; } }"); } [Fact] - public void TestFirstFromClauseExpression() + public void AnonymousMethod2() + { + TestSpan( +@"class C +{ + void Foo() + { + Func f = delegate { [|$$return 1;|] }; + } +}"); + } + + #endregion + + #region Queries + + [Fact] + public void FirstFromClauseExpression() { TestSpan( @"class C @@ -435,7 +465,7 @@ void Foo() } [Fact] - public void TestSecondFromClauseExpression() + public void SecondFromClauseExpression() { TestSpan( @"class C @@ -450,7 +480,7 @@ void Foo() } [Fact] - public void TestFromInQueryContinuation1() + public void FromInQueryContinuation1() { TestSpan( @"class C @@ -467,7 +497,7 @@ void Foo() } [Fact] - public void TestFromInQueryContinuation2() + public void FromInQueryContinuation2() { TestSpan( @"class C @@ -484,7 +514,7 @@ void Foo() } [Fact] - public void TestJoinClauseLeftExpression() + public void JoinClauseLeftExpression() { TestSpan( @"class C @@ -499,7 +529,7 @@ void Foo() } [Fact] - public void TestJoinClauseRightExpression() + public void JoinClauseRightExpression() { TestSpan( @"class C @@ -514,7 +544,7 @@ void Foo() } [Fact] - public void TestLetClauseExpression() + public void LetClauseExpression() { TestSpan( @"class C @@ -530,7 +560,7 @@ void Foo() } [Fact] - public void TestWhereClauseExpression() + public void WhereClauseExpression() { TestSpan( @"class C @@ -546,7 +576,7 @@ void Foo() } [Fact] - public void TestWhereClauseKeyword() + public void WhereClauseKeyword() { TestSpan( @"class C @@ -561,7 +591,7 @@ void Foo() } [Fact] - public void TestSimpleOrdering1() + public void SimpleOrdering1() { TestSpan( @"class C @@ -577,7 +607,7 @@ void Foo() } [Fact] - public void TestSimpleOrdering2() + public void SimpleOrdering2() { TestSpan( @"class C @@ -593,7 +623,7 @@ void Foo() } [Fact] - public void TestAscendingOrdering1() + public void AscendingOrdering1() { TestSpan( @"class C @@ -609,7 +639,7 @@ void Foo() } [Fact] - public void TestAscendingOrdering2() + public void AscendingOrdering2() { TestSpan( @"class C @@ -625,7 +655,7 @@ void Foo() } [Fact] - public void TestDescendingOrdering1() + public void DescendingOrdering1() { TestSpan( @"class C @@ -641,7 +671,7 @@ void Foo() } [Fact] - public void TestDescendingOrdering2() + public void DescendingOrdering2() { TestSpan( @"class C @@ -657,19 +687,19 @@ void Foo() } [Fact] - public void TestOrderByKeyword() + public void OrderByKeyword() { TestSpan("class C { void M() { from string s in null ord$$erby [|s.A|] ascending } }"); } [Fact] - public void TestAscendingKeyword() + public void AscendingKeyword() { TestSpan("class C { void M() { from string s in null orderby [|s.A|] $$ascending } }"); } [Fact] - public void TestSelectExpression() + public void SelectExpression() { TestSpan( @"class C @@ -685,12 +715,12 @@ void Foo() } [Fact] - public void TestAnonymousTypeAfterSelect() + public void AnonymousTypeAfterSelect() { TestSpan( @"class C { - public void Test() + public void () { var q = from c in categories @@ -701,7 +731,7 @@ public void Test() } [Fact] - public void TestGroupExpression() + public void GroupExpression() { TestSpan( @"class C @@ -718,7 +748,7 @@ void Foo() } [Fact] - public void TestGroupByKeyword() + public void GroupByKeyword() { TestSpan( @"class C @@ -735,7 +765,7 @@ void Foo() } [Fact] - public void TestGroupByExpression() + public void GroupByExpression() { TestSpan( @"class C @@ -752,7 +782,224 @@ void Foo() } [Fact] - public void TestFieldDeclarator_WithoutInitializer1() + public void InFrontOfFirstFromClause() + { + TestSpan( +@"class C +{ + void Foo() + { + [|var q = + $$ from x in blah() + from y in zap() + let m = quux() + join a in alpha on left().expr() equals right.expr() + orderby foo, expr().expr() descending + group bar().foo() by blah().zap() into g + select y.blah();|] + } +}"); + } + + [Fact] + public void InFrontOfSecondFromClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + $$ from y in [|zap()|] + let m = quux() + join a in alpha on left().expr() equals right.expr() + orderby foo, expr().expr() descending + group bar().foo() by blah().zap() into g + select y.blah(); + } +}"); + } + + [Fact] + public void InFrontOfLetClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + from y in zap() + $$ let m = [|quux()|] + join a in alpha on left().expr() equals right.expr() + orderby foo, expr().expr() descending + group bar().foo() by blah().zap() into g + select y.blah(); + } +}"); + } + + [Fact] + public void InFrontOfJoinClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + from y in zap() + let m = quux() + $$ join a in alpha on [|left().expr()|] equals right.expr() + orderby foo, expr().expr() descending + group bar().foo() by blah().zap() into g + select y.blah(); + } +}"); + } + + [Fact] + public void InFrontOfOrderByClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + from y in zap() + let m = quux() + join a in alpha on left().expr() equals right.expr() + $$ orderby [|foo|], expr().expr() descending + group bar().foo() by blah().zap() into g + select y.blah(); + } +}"); + } + + [Fact] + public void InFrontOfGroupByClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + from y in zap() + let m = quux() + join a in alpha on left().expr() equals right.expr() + orderby foo, expr().expr() descending + $$ group [|bar().foo()|] by blah().zap() into g + select y.blah(); + }"); + } + + [Fact] + public void InFrontOfSelectClause() + { + TestSpan( +@"class C +{ + void Foo() + { + var q = + from x in blah() + from y in zap() + let m = quux() + join a in alpha on left().expr() equals right.expr() + orderby foo, expr().expr() descending + group bar().foo() by blah().zap() into g + $$ select [|y.blah()|]; + }"); + } + + [Fact] + public void Select1() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => from x in new[] { 1 } select [|$$x|]; +} +"); + } + + [Fact] + public void Select_NoLambda1() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => [|from x in new[] { 1 } where x > 0 select $$x|]; +} +"); + } + + [Fact] + public void Select_NoLambda2() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => [|from x in new[] { 1 } select x into y orderby y select $$y|]; +} +"); + } + + [Fact] + public void GroupBy1() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => from x in new[] { 1 } group x by [|$$x|]; +} +"); + } + + [Fact] + public void GroupBy_NoLambda1() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => [|from x in new[] { 1 } group $$x by x|]; +} +"); + } + + [Fact] + public void GroupBy_NoLambda2() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => [|from x in new[] { 1 } group $$x by x + 1 into y group y by y.Key + 2|]; +} +"); + } + + [Fact] + public void GroupBy_NoLambda3() + { + TestSpan( +@"class C +{ + IEnumerable Foo() => [|from x in new[] { 1 } group x by x + 1 into y group $$y by y.Key + 2|]; +} +"); + } + + #endregion + + [Fact] + public void FieldDeclarator_WithoutInitializer1() { TestMissing( @"class C @@ -762,7 +1009,7 @@ public void TestFieldDeclarator_WithoutInitializer1() } [Fact] - public void TestFieldDeclarator_WithoutInitializer2() + public void FieldDeclarator_WithoutInitializer2() { TestMissing( @"class C @@ -772,7 +1019,7 @@ public void TestFieldDeclarator_WithoutInitializer2() } [Fact] - public void TestFieldDeclarator1() + public void FieldDeclarator1() { TestSpan( @"class C @@ -782,7 +1029,7 @@ public void TestFieldDeclarator1() } [Fact] - public void TestFieldDeclarator2() + public void FieldDeclarator2() { TestSpan( @"class C @@ -792,7 +1039,7 @@ public void TestFieldDeclarator2() } [Fact] - public void TestFieldDeclarator3() + public void FieldDeclarator3() { TestSpan( @"class C @@ -803,7 +1050,7 @@ public void TestFieldDeclarator3() } [Fact] - public void TestFieldDeclarator4() + public void FieldDeclarator4() { TestSpan( @"class C @@ -813,7 +1060,7 @@ public void TestFieldDeclarator4() } [Fact] - public void TestFieldDeclarator5() + public void FieldDeclarator5() { TestSpan( @"class C @@ -823,50 +1070,50 @@ public void TestFieldDeclarator5() } [Fact] - public void TestConstVariableDeclarator0() + public void ConstVariableDeclarator0() { TestMissing("class C { void Foo() { const int a = $$1; } }"); } [Fact] - public void TestConstVariableDeclarator1() + public void ConstVariableDeclarator1() { TestMissing("class C { void Foo() { const $$int a = 1; } }"); } [Fact] - public void TestConstVariableDeclarator2() + public void ConstVariableDeclarator2() { TestMissing("class C { void Foo() { $$const int a = 1; } }"); } [Fact] - public void TestConstFieldVariableDeclarator0() + public void ConstFieldVariableDeclarator0() { TestMissing("class C { const int a = $$1; }"); } [Fact] - public void TestConstFieldVariableDeclarator1() + public void ConstFieldVariableDeclarator1() { TestMissing("class C { const $$int a = 1; }"); } [Fact] - public void TestConstFieldVariableDeclarator2() + public void ConstFieldVariableDeclarator2() { TestMissing("class C { $$const int a = 1; }"); } [Fact] [WorkItem(538777)] - public void TestVariableDeclarator0() + public void VariableDeclarator0() { TestMissing("class C { void Foo() { int$$ } }"); } [Fact] - public void TestVariableDeclarator1() + public void VariableDeclarator1() { TestMissing( @"class C @@ -879,7 +1126,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator2a() + public void VariableDeclarator2a() { TestSpan( @"class C @@ -892,7 +1139,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator2b() + public void VariableDeclarator2b() { TestSpan( @"class C @@ -905,7 +1152,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator2c() + public void VariableDeclarator2c() { TestSpan( @"class C @@ -918,7 +1165,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator3a() + public void VariableDeclarator3a() { TestSpan( @"class C @@ -931,7 +1178,7 @@ void Foo() } [Fact, Trait(Traits.Feature, Traits.Features.DebuggingBreakpoints)] - public void TestVariableDeclarator3b() + public void VariableDeclarator3b() { TestSpan( @"class C @@ -944,7 +1191,7 @@ void Foo() } [Fact, Trait(Traits.Feature, Traits.Features.DebuggingBreakpoints)] - public void TestVariableDeclarator3c() + public void VariableDeclarator3c() { TestSpan( @"class C @@ -957,7 +1204,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator4() + public void VariableDeclarator4() { TestSpan( @"class C @@ -970,7 +1217,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator5() + public void VariableDeclarator5() { TestSpan( @"class C @@ -980,7 +1227,7 @@ public void TestVariableDeclarator5() } [Fact] - public void TestVariableDeclarator6() + public void VariableDeclarator6() { TestSpan( @"class C @@ -990,7 +1237,7 @@ public void TestVariableDeclarator6() } [Fact] - public void TestVariableDeclarator7() + public void VariableDeclarator7() { TestSpan( @"class C @@ -1000,7 +1247,7 @@ public void TestVariableDeclarator7() } [Fact] - public void TestVariableDeclarator8() + public void VariableDeclarator8() { TestSpan( @"class C @@ -1010,7 +1257,7 @@ public void TestVariableDeclarator8() } [Fact] - public void TestVariableDeclarator9() + public void VariableDeclarator9() { TestSpan( @"class C @@ -1020,13 +1267,13 @@ public void TestVariableDeclarator9() } [Fact] - public void TestVariableDeclarator10() + public void VariableDeclarator10() { TestSpan("class C { void M() { [|int i = 0$$;|] } }"); } [Fact] - public void TestVariableDeclarator_Separators0() + public void VariableDeclarator_Separators0() { TestSpan( @"class C @@ -1039,7 +1286,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators1() + public void VariableDeclarator_Separators1() { TestSpan( @"class C @@ -1052,7 +1299,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators2() + public void VariableDeclarator_Separators2() { TestSpan( @"class C @@ -1065,7 +1312,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators3() + public void VariableDeclarator_Separators3() { TestSpan( @"class C @@ -1078,7 +1325,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators4() + public void VariableDeclarator_Separators4() { TestSpan( @"class C @@ -1091,7 +1338,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators5() + public void VariableDeclarator_Separators5() { TestSpan( @"class C @@ -1104,7 +1351,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators6() + public void VariableDeclarator_Separators6() { TestSpan( @"class C @@ -1117,7 +1364,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators7() + public void VariableDeclarator_Separators7() { TestSpan( @"class C @@ -1130,7 +1377,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators8() + public void VariableDeclarator_Separators8() { TestSpan( @"class C @@ -1143,7 +1390,7 @@ void Foo() } [Fact] - public void TestVariableDeclarator_Separators9() + public void VariableDeclarator_Separators9() { TestSpan( @"class C @@ -1156,7 +1403,7 @@ void Foo() } [Fact] - public void TestEventFieldDeclarator1() + public void EventFieldDeclarator1() { TestSpan( @"class C @@ -1166,7 +1413,7 @@ public void TestEventFieldDeclarator1() } [Fact] - public void TestEventFieldDeclarator2() + public void EventFieldDeclarator2() { TestSpan( @"class C @@ -1176,7 +1423,7 @@ public void TestEventFieldDeclarator2() } [Fact] - public void TestEventFieldDeclarator3() + public void EventFieldDeclarator3() { TestSpan( @"class C @@ -1186,7 +1433,7 @@ public void TestEventFieldDeclarator3() } [Fact] - public void TestEventFieldDeclarator4() + public void EventFieldDeclarator4() { TestSpan( @"class C @@ -1196,7 +1443,7 @@ public void TestEventFieldDeclarator4() } [Fact] - public void TestEventFieldDeclarator5() + public void EventFieldDeclarator5() { TestSpan( @"class C @@ -1206,7 +1453,7 @@ public void TestEventFieldDeclarator5() } [Fact] - public void TestEventFieldDeclarator6() + public void EventFieldDeclarator6() { TestSpan( @"class C @@ -1216,7 +1463,7 @@ public void TestEventFieldDeclarator6() } [Fact] - public void TestEventFieldDeclarator7() + public void EventFieldDeclarator7() { TestSpan( @"class C @@ -1226,7 +1473,7 @@ public void TestEventFieldDeclarator7() } [Fact] - public void TestEventFieldDeclarator8() + public void EventFieldDeclarator8() { TestSpan( @"class C @@ -1236,25 +1483,25 @@ public void TestEventFieldDeclarator8() } [Fact] - public void TestEventAccessorAdd() + public void EventAccessorAdd() { TestSpan("class C { eve$$nt Action Foo { add [|{|] } remove { } } }"); } [Fact] - public void TestEventAccessorAdd2() + public void EventAccessorAdd2() { TestSpan("class C { event Action Foo { ad$$d [|{|] } remove { } } }"); } [Fact] - public void TestEventAccessorRemove() + public void EventAccessorRemove() { TestSpan("class C { event Action Foo { add { } $$remove [|{|] } } }"); } [Fact] - public void TestElseClauseWithBlock() + public void ElseClauseWithBlock() { TestSpan( @"class C @@ -1272,7 +1519,7 @@ void Foo() } [Fact] - public void TestElseClauseWithStatement() + public void ElseClauseWithStatement() { TestSpan( @"class C @@ -1289,7 +1536,7 @@ void Foo() } [Fact] - public void TestElseIf() + public void ElseIf() { TestSpan( @"class C @@ -1306,7 +1553,7 @@ void Foo() } [Fact] - public void TestEmptyCatch() + public void EmptyCatch() { TestSpan( @"class C @@ -1324,7 +1571,7 @@ void Foo() } [Fact] - public void TestCatchWithType() + public void CatchWithType() { TestSpan( @"class C @@ -1342,7 +1589,7 @@ void Foo() } [Fact] - public void TestCatchWithTypeInType() + public void CatchWithTypeInType() { TestSpan( @"class C @@ -1360,7 +1607,7 @@ void Foo() } [Fact] - public void TestCatchWithTypeAndNameInType() + public void CatchWithTypeAndNameInType() { TestSpan( @"class C @@ -1378,7 +1625,7 @@ void Foo() } [Fact] - public void TestCatchWithTypeAndNameInName() + public void CatchWithTypeAndNameInName() { TestSpan( @"class C @@ -1396,7 +1643,7 @@ void Foo() } [Fact] - public void TestFilter1() + public void Filter1() { TestSpan( @"class C @@ -1414,7 +1661,7 @@ void Foo() } [Fact] - public void TestFilter3() + public void Filter3() { TestSpan( @"class C @@ -1432,7 +1679,7 @@ void Foo() } [Fact] - public void TestFilter4() + public void Filter4() { TestSpan( @"class C @@ -1450,7 +1697,7 @@ void Foo() } [Fact] - public void TestFilter5() + public void Filter5() { TestSpan( @"class C @@ -1468,7 +1715,7 @@ void Foo() } [Fact] - public void TestSimpleFinally() + public void SimpleFinally() { TestSpan( @"class C @@ -1486,7 +1733,7 @@ void Foo() } [Fact] - public void TestFinallyWithCatch() + public void FinallyWithCatch() { TestSpan( @"class C @@ -1507,7 +1754,7 @@ void Foo() } [Fact] - public void TestSwitchLabelWithBlock() + public void SwitchLabelWithBlock() { TestSpan( @"class C @@ -1525,7 +1772,7 @@ void Foo() } [Fact] - public void TestSwitchLabelWithStatement() + public void SwitchLabelWithStatement() { TestSpan( @"class C @@ -1542,7 +1789,7 @@ void Foo() } [Fact] - public void TestSwitchLabelWithStatement2() + public void SwitchLabelWithStatement2() { TestSpan( @"class C @@ -1560,13 +1807,13 @@ void Foo() } [Fact] - public void TestSwitchLabelWithoutStatement() + public void SwitchLabelWithoutStatement() { TestSpan("class C { void M() { [|switch |]{ case 1$$: } } }"); } [Fact] - public void TestMultipleLabelsOnFirstLabel() + public void MultipleLabelsOnFirstLabel() { TestSpan( @"class C @@ -1588,7 +1835,7 @@ void Foo() } [Fact] - public void TestMultipleLabelsOnSecondLabel() + public void MultipleLabelsOnSecondLabel() { TestSpan( @"class C @@ -1610,7 +1857,7 @@ void Foo() } [Fact] - public void TestMultipleLabelsOnLabelWithDefault() + public void MultipleLabelsOnLabelWithDefault() { TestSpan( @"class C @@ -1632,7 +1879,7 @@ void Foo() } [Fact] - public void TestMultipleLabelsOnDefault() + public void MultipleLabelsOnDefault() { TestSpan( @"class C @@ -1654,7 +1901,7 @@ void Foo() } [Fact] - public void TestBlockBeforeStartToken() + public void BlockBeforeStartToken() { TestSpan( @"class C @@ -1667,7 +1914,7 @@ void Foo() } [Fact] - public void TestBlockBeforeStartToken2() + public void BlockBeforeStartToken2() { TestSpan( @"class C @@ -1680,7 +1927,7 @@ void Foo() } [Fact] - public void TestBlockAfterStartToken() + public void BlockAfterStartToken() { TestSpan( @"class C @@ -1693,7 +1940,7 @@ void Foo() } [Fact] - public void TestBlockAfterStartToken2() + public void BlockAfterStartToken2() { TestSpan( @"class C @@ -1706,7 +1953,7 @@ void Foo() } [Fact] - public void TestBlockBeforeEndToken1() + public void BlockBeforeEndToken1() { TestSpan( @"class C @@ -1718,7 +1965,7 @@ void Foo() } [Fact] - public void TestBlockBeforeEndToken2() + public void BlockBeforeEndToken2() { TestSpan( @"class C @@ -1730,7 +1977,7 @@ void Foo() } [Fact] - public void TestBlockAfterEndToken1() + public void BlockAfterEndToken1() { TestSpan( @"class C @@ -1742,7 +1989,7 @@ void Foo() } [Fact] - public void TestBlockAfterEndToken2() + public void BlockAfterEndToken2() { TestSpan( @"class C @@ -1754,7 +2001,7 @@ void Foo() } [Fact] - public void TestSingleDeclarationOnType() + public void SingleDeclarationOnType() { TestMissing( @"class C @@ -1767,7 +2014,7 @@ void Foo() } [Fact] - public void TestMutlipleDeclarationsOnType() + public void MutlipleDeclarationsOnType() { TestSpan( @"class C @@ -1780,7 +2027,7 @@ void Foo() } [Fact] - public void TestLabel() + public void Label() { TestSpan( @"class C @@ -1794,7 +2041,7 @@ void Foo() } [Fact] - public void TestWhileInWhile() + public void WhileInWhile() { TestSpan( @"class C @@ -1809,7 +2056,7 @@ void Foo() } [Fact] - public void TestWhileInExpr() + public void WhileInExpr() { TestSpan( @"class C @@ -1824,7 +2071,7 @@ void Foo() } [Fact] - public void TestOnWhileBlock() + public void OnWhileBlock() { TestSpan( @"class C @@ -1839,7 +2086,7 @@ void Foo() } [Fact] - public void TestOnDoKeyword() + public void OnDoKeyword() { TestSpan( @"class C @@ -1855,7 +2102,7 @@ void Foo() } [Fact] - public void TestOnDoBlock() + public void OnDoBlock() { TestSpan( @"class C @@ -1871,7 +2118,7 @@ void Foo() } [Fact] - public void TestOnDoWhile() + public void OnDoWhile() { TestSpan( @"class C @@ -1887,7 +2134,7 @@ void Foo() } [Fact] - public void TestOnDoWhile_MissingSemicolon() + public void OnDoWhile_MissingSemicolon() { TestSpan( @"class C @@ -1903,7 +2150,7 @@ void Foo() } [Fact] - public void TestOnDoExpression() + public void OnDoExpression() { TestSpan( @"class C @@ -1919,7 +2166,7 @@ void Foo() } [Fact] - public void TestOnForWithDeclaration1() + public void OnForWithDeclaration1() { TestSpan( @"class C @@ -1934,7 +2181,7 @@ void Foo() } [Fact] - public void TestOnForWithDeclaration2() + public void OnForWithDeclaration2() { TestSpan( @"class C @@ -1949,7 +2196,7 @@ void Foo() } [Fact] - public void TestOnForWithCondition() + public void OnForWithCondition() { TestSpan( @"class C @@ -1964,7 +2211,7 @@ void Foo() } [Fact] - public void TestOnForWithIncrementor1() + public void OnForWithIncrementor1() { TestSpan( @"class C @@ -1979,7 +2226,7 @@ void Foo() } [Fact] - public void TestOnForWithIncrementor2() + public void OnForWithIncrementor2() { TestSpan( @"class C @@ -1994,7 +2241,7 @@ void Foo() } [Fact] - public void TestOnEmptyFor() + public void OnEmptyFor() { TestSpan( @"class C @@ -2009,7 +2256,7 @@ void Foo() } [Fact] - public void TestOnForEachKeyword1() + public void OnForEachKeyword1() { TestSpan( @"class C @@ -2024,7 +2271,7 @@ void Foo() } [Fact] - public void TestOnForEachKeyword2() + public void OnForEachKeyword2() { TestSpan( @"class C @@ -2039,7 +2286,7 @@ void Foo() } [Fact] - public void TestOnForEachKeyword3() + public void OnForEachKeyword3() { TestSpan( @"class C @@ -2055,7 +2302,7 @@ void Foo() } [Fact] - public void TestOnForEachKeyword4() + public void OnForEachKeyword4() { TestSpan( @"class C @@ -2071,7 +2318,7 @@ void Foo() } [Fact] - public void TestOnForEachKeyword5() + public void OnForEachKeyword5() { TestSpan( @"class C @@ -2086,7 +2333,7 @@ void Foo() } [Fact] - public void TestOnForEachType1() + public void OnForEachType1() { TestSpan( @"class C @@ -2102,7 +2349,7 @@ void Foo() } [Fact] - public void TestOnForEachType2() + public void OnForEachType2() { TestSpan( @"class C @@ -2117,7 +2364,7 @@ void Foo() } [Fact] - public void TestOnForEachIdentifier() + public void OnForEachIdentifier() { TestSpan( @"class C @@ -2132,7 +2379,7 @@ void Foo() } [Fact] - public void TestOnForEachIn1() + public void OnForEachIn1() { TestSpan( @"class C @@ -2147,7 +2394,7 @@ void Foo() } [Fact] - public void TestOnForEachIn2() + public void OnForEachIn2() { TestSpan( @"class C @@ -2163,7 +2410,7 @@ void Foo() } [Fact] - public void TestOnForEachIn3() + public void OnForEachIn3() { TestSpan( @"class C @@ -2180,7 +2427,7 @@ void Foo() } [Fact] - public void TestOnForEachExpr1() + public void OnForEachExpr1() { TestSpan( @"class C @@ -2195,7 +2442,7 @@ void Foo() } [Fact] - public void TestOnForEachExpr2() + public void OnForEachExpr2() { TestSpan( @"class C @@ -2211,7 +2458,7 @@ void Foo() } [Fact] - public void TestOnForEachExpr3() + public void OnForEachExpr3() { TestSpan( @"class C @@ -2228,7 +2475,7 @@ void Foo() } [Fact] - public void TestOnForEachStatement() + public void OnForEachStatement() { TestSpan( @"class C @@ -2243,7 +2490,7 @@ void Foo() } [Fact] - public void TestOnForEachBlock1() + public void OnForEachBlock1() { TestSpan( @"class C @@ -2258,7 +2505,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDecl1() + public void OnUsingWithDecl1() { TestSpan( @"class C @@ -2273,7 +2520,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDecl2() + public void OnUsingWithDecl2() { TestSpan( @"class C @@ -2288,7 +2535,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDeclType() + public void OnUsingWithDeclType() { TestSpan( @"class C @@ -2303,7 +2550,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDeclIdentifier1() + public void OnUsingWithDeclIdentifier1() { TestSpan( @"class C @@ -2318,7 +2565,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDeclIdentifier2() + public void OnUsingWithDeclIdentifier2() { TestSpan( @"class C @@ -2333,7 +2580,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDeclIdentifier3() + public void OnUsingWithDeclIdentifier3() { TestSpan( @"class C @@ -2348,7 +2595,7 @@ void Foo() } [Fact] - public void TestOnUsingWithDeclExpression() + public void OnUsingWithDeclExpression() { TestSpan( @"class C @@ -2363,7 +2610,7 @@ void Foo() } [Fact] - public void TestOnUsingWithExpression1() + public void OnUsingWithExpression1() { TestSpan( @"class C @@ -2378,7 +2625,7 @@ void Foo() } [Fact] - public void TestOnUsingWithExpression2() + public void OnUsingWithExpression2() { TestSpan( @"class C @@ -2393,7 +2640,7 @@ void Foo() } [Fact] - public void TestOnFixed1() + public void OnFixed1() { TestSpan( @"class C @@ -2408,7 +2655,7 @@ void Foo() } [Fact] - public void TestOnFixed2() + public void OnFixed2() { TestSpan( @"class C @@ -2423,7 +2670,7 @@ void Foo() } [Fact] - public void TestOnFixed3() + public void OnFixed3() { TestSpan( @"class C @@ -2438,7 +2685,7 @@ void Foo() } [Fact] - public void TestOnFixed4() + public void OnFixed4() { TestSpan( @"class C @@ -2453,7 +2700,7 @@ void Foo() } [Fact] - public void TestOnFixed5() + public void OnFixed5() { TestSpan( @"class C @@ -2468,7 +2715,7 @@ void Foo() } [Fact] - public void TestOnFixed6() + public void OnFixed6() { TestSpan( @"class C @@ -2483,7 +2730,7 @@ void Foo() } [Fact] - public void TestOnChecked1() + public void OnChecked1() { TestSpan( @"class C @@ -2498,7 +2745,7 @@ void Foo() } [Fact] - public void TestOnUnchecked1() + public void OnUnchecked1() { TestSpan( @"class C @@ -2513,7 +2760,7 @@ void Foo() } [Fact] - public void TestOnUnsafe1() + public void OnUnsafe1() { TestSpan( @"class C @@ -2528,7 +2775,7 @@ void Foo() } [Fact] - public void TestOnLock1() + public void OnLock1() { TestSpan( @"class C @@ -2543,7 +2790,7 @@ void Foo() } [Fact] - public void TestOnLock2() + public void OnLock2() { TestSpan( @"class C @@ -2558,7 +2805,7 @@ void Foo() } [Fact] - public void TestOnIf1() + public void OnIf1() { TestSpan( @"class C @@ -2573,7 +2820,7 @@ void Foo() } [Fact] - public void TestOnIf2() + public void OnIf2() { TestSpan( @"class C @@ -2588,7 +2835,7 @@ void Foo() } [Fact] - public void TestOnIfBlock() + public void OnIfBlock() { TestSpan( @"class C @@ -2603,7 +2850,7 @@ void Foo() } [Fact] - public void TestOnSwitch1() + public void OnSwitch1() { TestSpan( @"class C @@ -2620,7 +2867,7 @@ void Foo() } [Fact] - public void TestOnSwitch2() + public void OnSwitch2() { TestSpan( @"class C @@ -2637,7 +2884,7 @@ void Foo() } [Fact] - public void TestOnSwitch3() + public void OnSwitch3() { TestSpan( @"class C @@ -2654,7 +2901,7 @@ void Foo() } [Fact] - public void TestOnSwitch4() + public void OnSwitch4() { TestSpan( @"class C @@ -2671,7 +2918,7 @@ void Foo() } [Fact] - public void TestOnTry1() + public void OnTry1() { TestSpan( @"class C @@ -2689,7 +2936,7 @@ void Foo() } [Fact] - public void TestOnTry2() + public void OnTry2() { TestSpan( @"class C @@ -2707,7 +2954,7 @@ void Foo() } [Fact] - public void TestOnGotoStatement1() + public void OnGotoStatement1() { TestSpan( @"class C @@ -2720,7 +2967,7 @@ void Foo() } [Fact] - public void TestOnGotoStatement2() + public void OnGotoStatement2() { TestSpan( @"class C @@ -2733,7 +2980,7 @@ void Foo() } [Fact] - public void TestOnGotoCaseStatement1() + public void OnGotoCaseStatement1() { TestSpan( @"class C @@ -2750,7 +2997,7 @@ void Foo() } [Fact] - public void TestOnGotoCaseStatement2() + public void OnGotoCaseStatement2() { TestSpan( @"class C @@ -2767,7 +3014,7 @@ void Foo() } [Fact] - public void TestOnGotoCaseStatement3() + public void OnGotoCaseStatement3() { TestSpan( @"class C @@ -2784,7 +3031,7 @@ void Foo() } [Fact] - public void TestOnGotoDefault1() + public void OnGotoDefault1() { TestSpan( @"class C @@ -2801,7 +3048,7 @@ void Foo() } [Fact] - public void TestOnGotoDefault2() + public void OnGotoDefault2() { TestSpan( @"class C @@ -2818,7 +3065,7 @@ void Foo() } [Fact] - public void TestOnBreak1() + public void OnBreak1() { TestSpan( @"class C @@ -2834,7 +3081,7 @@ void Foo() } [Fact] - public void TestOnContinue1() + public void OnContinue1() { TestSpan( @"class C @@ -2850,7 +3097,7 @@ void Foo() } [Fact] - public void TestOnReturn1() + public void OnReturn1() { TestSpan( @"class C @@ -2863,7 +3110,7 @@ void Foo() } [Fact] - public void TestOnReturn2() + public void OnReturn2() { TestSpan( @"class C @@ -2876,7 +3123,7 @@ void Foo() } [Fact] - public void TestOnReturn3() + public void OnReturn3() { TestSpan( @"class C @@ -2889,7 +3136,7 @@ void Foo() } [Fact] - public void TestOnYieldReturn1() + public void OnYieldReturn1() { TestSpan( @"class C @@ -2902,7 +3149,7 @@ void Foo() } [Fact] - public void TestOnYieldReturn2() + public void OnYieldReturn2() { TestSpan( @"class C @@ -2915,7 +3162,7 @@ void Foo() } [Fact] - public void TestOnYieldReturn3() + public void OnYieldReturn3() { TestSpan( @"class C @@ -2928,7 +3175,7 @@ void Foo() } [Fact] - public void TestOnYieldBreak1() + public void OnYieldBreak1() { TestSpan( @"class C @@ -2941,7 +3188,7 @@ void Foo() } [Fact] - public void TestOnYieldBreak2() + public void OnYieldBreak2() { TestSpan( @"class C @@ -2954,7 +3201,7 @@ void Foo() } [Fact] - public void TestOnThrow1() + public void OnThrow1() { TestSpan( @"class C @@ -2967,7 +3214,7 @@ void Foo() } [Fact] - public void TestOnThrow2() + public void OnThrow2() { TestSpan( @"class C @@ -2980,7 +3227,7 @@ void Foo() } [Fact] - public void TestOnThrow3() + public void OnThrow3() { TestSpan( @"class C @@ -2993,7 +3240,7 @@ void Foo() } [Fact] - public void TestOnThrow4() + public void OnThrow4() { TestSpan( @"class C @@ -3006,7 +3253,7 @@ void Foo() } [Fact] - public void TestOnExpressionStatement1() + public void OnExpressionStatement1() { TestSpan( @"class C @@ -3019,7 +3266,7 @@ void Foo() } [Fact] - public void TestOnEmptyStatement1() + public void OnEmptyStatement1() { TestSpan( @"class C @@ -3032,7 +3279,7 @@ void Foo() } [Fact] - public void TestOnEmptyStatement2() + public void OnEmptyStatement2() { TestSpan( @"class C @@ -3048,7 +3295,7 @@ void Foo() } [Fact] - public void TestOnPropertyAccessor1() + public void OnPropertyAccessor1() { TestSpan( @"class C @@ -3063,7 +3310,7 @@ int Foo } [Fact] - public void TestOnPropertyAccessor2() + public void OnPropertyAccessor2() { TestSpan( @"class C @@ -3076,7 +3323,7 @@ int Foo } [Fact] - public void TestOnPropertyAccessor3() + public void OnPropertyAccessor3() { TestSpan( @"class C @@ -3095,7 +3342,7 @@ int Foo } [Fact] - public void TestOnPropertyAccessor4() + public void OnPropertyAccessor4() { TestSpan( @"class C @@ -3108,7 +3355,7 @@ int Foo } [Fact] - public void TestOnProperty1() + public void OnProperty1() { TestSpan( @"class C @@ -3127,7 +3374,7 @@ public void TestOnProperty1() } [Fact] - public void TestOnProperty2() + public void OnProperty2() { TestSpan( @"class C @@ -3142,7 +3389,7 @@ public void TestOnProperty2() [WorkItem(932711)] [Fact] - public void TestOnPropertyWithInitializer() + public void OnPropertyWithInitializer() { TestSpan( @"class C @@ -3176,7 +3423,7 @@ public void TestOnPropertyWithInitializer() } [Fact] - public void TestOnPropertyExpressionBody1() + public void OnPropertyExpressionBody1() { TestSpan( @"class C @@ -3186,7 +3433,7 @@ public void TestOnPropertyExpressionBody1() } [Fact] - public void TestOnPropertyExpressionBody2() + public void OnPropertyExpressionBody2() { TestSpan( @"class C @@ -3196,7 +3443,7 @@ public void TestOnPropertyExpressionBody2() } [Fact] - public void TestOnPropertyExpressionBody3() + public void OnPropertyExpressionBody3() { TestSpan( @"class C @@ -3206,7 +3453,7 @@ public void TestOnPropertyExpressionBody3() } [Fact] - public void TestOnPropertyExpressionBody4() + public void OnPropertyExpressionBody4() { TestSpan( @"class C @@ -3216,7 +3463,7 @@ public void TestOnPropertyExpressionBody4() } [Fact] - public void TestOnIndexerExpressionBody1() + public void OnIndexerExpressionBody1() { TestSpan( @"class C @@ -3226,7 +3473,7 @@ public void TestOnIndexerExpressionBody1() } [Fact] - public void TestOnIndexer1() + public void OnIndexer1() { TestSpan( @"class C @@ -3245,7 +3492,7 @@ public void TestOnIndexer1() } [Fact] - public void TestOnIndexer2() + public void OnIndexer2() { TestSpan( @"class C @@ -3259,7 +3506,7 @@ public void TestOnIndexer2() } [Fact] - public void TestOnIndexerExpressionBody2() + public void OnIndexerExpressionBody2() { TestSpan( @"class C @@ -3269,7 +3516,7 @@ public void TestOnIndexerExpressionBody2() } [Fact] - public void TestOnIndexerExpressionBody3() + public void OnIndexerExpressionBody3() { TestSpan( @"class C @@ -3279,7 +3526,7 @@ public void TestOnIndexerExpressionBody3() } [Fact] - public void TestOnIndexerExpressionBody4() + public void OnIndexerExpressionBody4() { TestSpan( @"class C @@ -3289,7 +3536,7 @@ public void TestOnIndexerExpressionBody4() } [Fact] - public void TestOnIndexerExpressionBody5() + public void OnIndexerExpressionBody5() { TestSpan( @"class C @@ -3299,7 +3546,7 @@ public void TestOnIndexerExpressionBody5() } [Fact] - public void TestOnMethod1() + public void OnMethod1() { TestSpan( @"class C @@ -3311,7 +3558,7 @@ public void TestOnMethod1() } [Fact] - public void TestOnMethod2() + public void OnMethod2() { TestSpan( @"class C @@ -3323,7 +3570,7 @@ public void TestOnMethod2() } [Fact] - public void TestOnMethod3() + public void OnMethod3() { TestSpan( @"class C @@ -3335,7 +3582,7 @@ void Foo(in$$t i) } [Fact] - public void TestOnMethod4() + public void OnMethod4() { TestSpan( @"class C @@ -3347,7 +3594,7 @@ void Foo(int $$i) } [Fact] - public void TestOnMethod5() + public void OnMethod5() { TestSpan( @"class C @@ -3359,7 +3606,7 @@ void Foo(int i = f$$oo) } [Fact] - public void TestOnMethodWithExpressionBody1() + public void OnMethodWithExpressionBody1() { TestSpan( @"class C @@ -3369,7 +3616,7 @@ public void TestOnMethodWithExpressionBody1() } [Fact] - public void TestOnMethodWithExpressionBody2() + public void OnMethodWithExpressionBody2() { TestSpan( @"class C @@ -3379,7 +3626,7 @@ public void TestOnMethodWithExpressionBody2() } [Fact] - public void TestOnMethodWithExpressionBody3() + public void OnMethodWithExpressionBody3() { TestSpan( @"class C @@ -3389,7 +3636,7 @@ public void TestOnMethodWithExpressionBody3() } [Fact] - public void TestOnMethodWithExpressionBody4() + public void OnMethodWithExpressionBody4() { TestSpan( @"class C @@ -3399,7 +3646,7 @@ public void TestOnMethodWithExpressionBody4() } [Fact] - public void TestMissingOnMethod() + public void MissingOnMethod() { TestMissing( @"class C @@ -3493,7 +3740,7 @@ public Derived() [WorkItem(543968)] [Fact] - public void TestConstructorInitializer() + public void ConstructorInitializer() { // a sequence point for base constructor call TestSpan( @@ -3508,7 +3755,7 @@ public Derived() } [Fact] - public void TestOnStaticConstructor() + public void OnStaticConstructor() { TestSpan( @"class C @@ -3520,7 +3767,7 @@ public void TestOnStaticConstructor() } [Fact] - public void TestOnDestructor() + public void OnDestructor() { TestSpan( @"class C @@ -3532,7 +3779,7 @@ public void TestOnDestructor() } [Fact] - public void TestOnOperator() + public void OnOperator() { TestSpan( @"class C @@ -3544,7 +3791,7 @@ public void TestOnOperator() } [Fact] - public void TestOnOperatorWithExpressionBody1() + public void OnOperatorWithExpressionBody1() { TestSpan( @"class C @@ -3554,7 +3801,7 @@ public void TestOnOperatorWithExpressionBody1() } [Fact] - public void TestOnOperatorWithExpressionBody2() + public void OnOperatorWithExpressionBody2() { TestSpan( @"class C @@ -3564,7 +3811,7 @@ public void TestOnOperatorWithExpressionBody2() } [Fact] - public void TestOnOperatorWithExpressionBody3() + public void OnOperatorWithExpressionBody3() { TestSpan( @"class C @@ -3574,7 +3821,7 @@ public void TestOnOperatorWithExpressionBody3() } [Fact] - public void TestOnOperatorWithExpressionBody4() + public void OnOperatorWithExpressionBody4() { TestSpan( @"class C @@ -3584,7 +3831,7 @@ public void TestOnOperatorWithExpressionBody4() } [Fact] - public void TestOnConversionOperator() + public void OnConversionOperator() { TestSpan( @"class C @@ -3596,7 +3843,7 @@ public void TestOnConversionOperator() } [Fact] - public void TestOnConversionOperatorWithExpressionBody1() + public void OnConversionOperatorWithExpressionBody1() { TestSpan( @"class C @@ -3606,7 +3853,7 @@ public void TestOnConversionOperatorWithExpressionBody1() } [Fact] - public void TestOnConversionOperatorWithExpressionBody2() + public void OnConversionOperatorWithExpressionBody2() { TestSpan( @"class C @@ -3616,7 +3863,7 @@ public void TestOnConversionOperatorWithExpressionBody2() } [Fact] - public void TestOnConversionOperatorWithExpressionBody3() + public void OnConversionOperatorWithExpressionBody3() { TestSpan( @"class C @@ -3626,7 +3873,7 @@ public void TestOnConversionOperatorWithExpressionBody3() } [Fact] - public void TestOnConversionOperatorWithExpressionBody4() + public void OnConversionOperatorWithExpressionBody4() { TestSpan( @"class C @@ -3635,147 +3882,9 @@ public void TestOnConversionOperatorWithExpressionBody4() }"); } - [Fact] - public void TestInFrontOfFirstFromClause() - { - TestSpan( -@"class C -{ - void Foo() - { - [|var q = - $$ from x in blah() - from y in zap() - let m = quux() - join a in alpha on left().expr() equals right.expr() - orderby foo, expr().expr() descending - group bar().foo() by blah().zap() into g - select y.blah();|] - } -}"); - } - - [Fact] - public void TestInFrontOfSecondFromClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - $$ from y in [|zap()|] - let m = quux() - join a in alpha on left().expr() equals right.expr() - orderby foo, expr().expr() descending - group bar().foo() by blah().zap() into g - select y.blah(); - } -}"); - } - - [Fact] - public void TestInFrontOfLetClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - from y in zap() - $$ let m = [|quux()|] - join a in alpha on left().expr() equals right.expr() - orderby foo, expr().expr() descending - group bar().foo() by blah().zap() into g - select y.blah(); - } -}"); - } - - [Fact] - public void TestInFrontOfJoinClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - from y in zap() - let m = quux() - $$ join a in alpha on [|left().expr()|] equals right.expr() - orderby foo, expr().expr() descending - group bar().foo() by blah().zap() into g - select y.blah(); - } -}"); - } - - [Fact] - public void TestInFrontOfOrderByClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - from y in zap() - let m = quux() - join a in alpha on left().expr() equals right.expr() - $$ orderby [|foo|], expr().expr() descending - group bar().foo() by blah().zap() into g - select y.blah(); - } -}"); - } - - [Fact] - public void TestInFrontOfGroupByClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - from y in zap() - let m = quux() - join a in alpha on left().expr() equals right.expr() - orderby foo, expr().expr() descending - $$ group [|bar().foo()|] by blah().zap() into g - select y.blah(); - }"); - } - - [Fact] - public void TestInFrontOfSelectClause() - { - TestSpan( -@"class C -{ - void Foo() - { - var q = - from x in blah() - from y in zap() - let m = quux() - join a in alpha on left().expr() equals right.expr() - orderby foo, expr().expr() descending - group bar().foo() by blah().zap() into g - $$ select [|y.blah()|]; - }"); - } - [WorkItem(3557, "DevDiv_Projects/Roslyn")] [Fact] - public void TestInFrontOfAttribute() + public void InFrontOfAttribute() { TestSpan( @"class C @@ -3789,7 +3898,7 @@ void Foo() [WorkItem(538058)] [Fact] - public void TestInInactivePPRegion() + public void InInactivePPRegion() { TestLine( @" @@ -3801,7 +3910,7 @@ public void TestInInactivePPRegion() [WorkItem(538777)] [Fact] - public void TestWithIncompleteDeclaration() + public void WithIncompleteDeclaration() { TestMissing( @" @@ -3816,7 +3925,7 @@ void Foo() [WorkItem(937290)] [Fact] - public void TestOnGetter() + public void OnGetter() { TestSpan( @"class C @@ -3845,7 +3954,7 @@ public void TestOnGetter() [WorkItem(937290)] [Fact] - public void TestOnSetter() + public void OnSetter() { TestSpan( @"class C diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs index f533ca96d84f68464895d5e10752b0f44ed7055f..44e68dc8bbb16cfd2a13dc9607b6fa7f22a64ff5 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs @@ -5435,6 +5435,72 @@ public void Queries_FromSelect_Update3() "Update [await b]@14 -> [await c]@14"); } + [Fact] + public void Queries_Select_Reduced1() + { + var src1 = @" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in new[] {1} where a > 0 select a; + } +} +"; + var src2 = @" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in new[] {1} where a > 0 select a + 1; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics(); + } + + [Fact] + public void Queries_Select_Reduced2() + { + var src1 = @" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in new[] {1} where a > 0 select a + 1; + } +} +"; + var src2 = @" +using System; +using System.Linq; +using System.Collections.Generic; + +class C +{ + void F() + { + var result = from a in new[] {1} where a > 0 select a; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics(); + } + [Fact] public void Queries_FromSelect_Delete() { @@ -5499,6 +5565,132 @@ public void Queries_OrderBy_Reorder() "Reorder [a.c ascending]@46 -> @30"); } + [Fact] + public void Queries_GroupBy_Reduced1() + { + var src1 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a by a; + } +} +"; + var src2 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a + 1.0 by a; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingQueryLambdaType, "group", CSharpFeaturesResources.GroupByClause)); + } + + [Fact] + public void Queries_GroupBy_Reduced2() + { + var src1 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a by a; + } +} +"; + var src2 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a + 1 by a; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics(); + } + + [Fact] + public void Queries_GroupBy_Reduced3() + { + var src1 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a + 1.0 by a; + } +} +"; + var src2 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a by a; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.ChangingQueryLambdaType, "group", CSharpFeaturesResources.GroupByClause)); + } + + [Fact] + public void Queries_GroupBy_Reduced4() + { + var src1 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a + 1 by a; + } +} +"; + var src2 = @" +using System; +using System.Linq; + +class C +{ + void F() + { + var result = from a in new[] {1} group a by a; + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics(); + } + [Fact] public void Queries_OrderBy_Continuation_Update() { @@ -5803,8 +5995,7 @@ void F() } "; var edits = GetTopEdits(src1, src2); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.ChangingQueryLambdaType, "select", CSharpFeaturesResources.SelectClause)); + edits.VerifySemanticDiagnostics(); } [Fact] diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index f0c54f528dd1968ea0d0692f080c3cf389150429..53dc287bc6a4fd03312bb7f8f0c1edb79d6992a9 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -239,6 +239,7 @@ + @@ -641,4 +642,4 @@ - + \ No newline at end of file diff --git a/src/VisualStudio/Core/Test/Debugging/BreakpointLocationValidatorTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/BreakpointSpansTests.vb similarity index 96% rename from src/VisualStudio/Core/Test/Debugging/BreakpointLocationValidatorTests.vb rename to src/EditorFeatures/VisualBasicTest/EditAndContinue/BreakpointSpansTests.vb index 9b317d8e1df5e98ca347588d37d58819138514a7..25168b97d2f8ce220f77ce7582d14137f8009d14 100644 --- a/src/VisualStudio/Core/Test/Debugging/BreakpointLocationValidatorTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/BreakpointSpansTests.vb @@ -4,18 +4,11 @@ Imports System.Threading Imports System.Xml.Linq Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions -Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces Imports Microsoft.CodeAnalysis.Text -Imports Microsoft.CodeAnalysis.VisualBasic -Imports Microsoft.CodeAnalysis.VisualBasic.Extensions -Imports Microsoft.CodeAnalysis.Editor.Implementation.Debugging -Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging -Imports Roslyn.Test.Utilities -Imports Roslyn.Utilities - -Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.UnitTests.Debugging + +Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests - Public Class BreakpointLocationValidatorTests + Public Class BreakpointSpansTests #Region "Helpers" @@ -48,7 +41,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.UnitTests.Debuggin Dim tree = SyntaxFactory.ParseSyntaxTree(source) Dim breakpointSpan As TextSpan - Dim hasBreakpoint = BreakpointGetter.TryGetBreakpointSpan(tree, + Dim hasBreakpoint = BreakpointSpans.TryGetBreakpointSpan(tree, position.Value, CancellationToken.None, breakpointSpan) @@ -69,6 +62,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.UnitTests.Debuggin End If End Sub + Private Sub TestAll(markup As String) Dim position As Integer = Nothing Dim expectedSpans As IList(Of TextSpan) = Nothing @@ -91,7 +85,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.UnitTests.Debuggin Dim lastSpanEnd = 0 While position < endPosition Dim span As TextSpan = Nothing - If root.TryGetEnclosingBreakpointSpan(position, span) AndAlso span.End > lastSpanEnd Then + If BreakpointSpans.TryGetEnclosingBreakpointSpan(root, position, span) AndAlso span.End > lastSpanEnd Then position = span.End lastSpanEnd = span.End Yield span @@ -3115,48 +3109,5 @@ End Class End Sub #End Region - - Public Sub TestSpanWithLength(markup As XElement, length As Integer) - Dim position As Integer? = Nothing - Dim expectedSpan As TextSpan? = Nothing - Dim source As String = Nothing - MarkupTestFile.GetPositionAndSpan(markup.NormalizedValue, source, position, expectedSpan) - - Using workspace = VisualBasicWorkspaceFactory.CreateWorkspaceFromLines(source) - Dim document = workspace.CurrentSolution.Projects.First.Documents.First - Dim result As BreakpointResolutionResult = BreakpointGetter.GetBreakpointAsync(document, position.Value, length, CancellationToken.None).WaitAndGetResult(CancellationToken.None) - Assert.True(expectedSpan.Value = result.TextSpan, - String.Format(vbCrLf & "Expected: {0} ""{1}""" & vbCrLf & "Actual: {2} ""{3}""", - expectedSpan.Value, - source.Substring(expectedSpan.Value.Start, expectedSpan.Value.Length), - result.TextSpan, - source.Substring(result.TextSpan.Start, result.TextSpan.Length))) - End Using - End Sub - - - - Public Sub TestBreakpointSpansMultipleMethods() - ' Normal case: debugger passing BP spans "sub Foo() end sub" - TestSpanWithLength( -class C - [|$$sub Foo()|] - end sub - - sub Bar() - end sub -end class, 20) - - ' Rare case: debugger passing BP spans "sub Foo() end sub sub Bar() end sub" - TestSpanWithLength( -class C - $$sub Foo() - end sub - - [|sub Bar()|] - end sub -end class, 35) - End Sub - End Class End Namespace diff --git a/src/Features/CSharp/CSharpFeatures.csproj b/src/Features/CSharp/CSharpFeatures.csproj index a1c22238ba281e05f8e52a80767dbbeb2d9e2d71..a1978dcc6ce6995db709f894def2fab3f3532324 100644 --- a/src/Features/CSharp/CSharpFeatures.csproj +++ b/src/Features/CSharp/CSharpFeatures.csproj @@ -77,12 +77,13 @@ - + + @@ -293,6 +294,7 @@ + diff --git a/src/Workspaces/CSharp/Portable/Extensions/BreakpointSpans.cs b/src/Features/CSharp/EditAndContinue/BreakpointSpans.cs similarity index 91% rename from src/Workspaces/CSharp/Portable/Extensions/BreakpointSpans.cs rename to src/Features/CSharp/EditAndContinue/BreakpointSpans.cs index ab0a0243e4325bdd6117a8dc0a5377b9beae9ee8..35fa26b99236e77610b50f6594b90fbcce27ff5b 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/BreakpointSpans.cs +++ b/src/Features/CSharp/EditAndContinue/BreakpointSpans.cs @@ -1,16 +1,55 @@ // 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.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.CSharp.Extensions; -namespace Microsoft.CodeAnalysis.CSharp.Extensions +namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue { - // TODO (tomat): Move to the compiler. Bug #764678. internal static class BreakpointSpans { + public static bool TryGetBreakpointSpan(SyntaxTree tree, int position, CancellationToken cancellationToken, out TextSpan breakpointSpan) + { + var source = tree.GetText(cancellationToken); + + // If the line is entirely whitespace, then don't set any breakpoint there. + var line = source.Lines.GetLineFromPosition(position); + if (IsBlank(line)) + { + breakpointSpan = default(TextSpan); + return false; + } + + // If the user is asking for breakpoint in an inactive region, then just create a line + // breakpoint there. + if (tree.IsInInactiveRegion(position, cancellationToken)) + { + breakpointSpan = default(TextSpan); + return true; + } + + var root = tree.GetRoot(cancellationToken); + return TryGetClosestBreakpointSpan(root, position, out breakpointSpan); + } + + private static bool IsBlank(TextLine line) + { + var text = line.ToString(); + + for (int i = 0; i < text.Length; i++) + { + if (!SyntaxFacts.IsWhitespace(text[i])) + { + return false; + } + } + + return true; + } + /// /// Given a syntax token determines a text span delimited by the closest applicable sequence points /// encompassing the token. @@ -18,7 +57,7 @@ internal static class BreakpointSpans /// /// If the span exists it is possible to place a breakpoint at the given position. /// - public static bool TryGetClosestBreakpointSpan(this SyntaxNode root, int position, out TextSpan span) + public static bool TryGetClosestBreakpointSpan(SyntaxNode root, int position, out TextSpan span) { SyntaxNode node = root.FindToken(position).Parent; while (node != null) @@ -657,11 +696,6 @@ private static bool IsBreakableExpression(ExpressionSyntax expression) Debug.Assert(((ArrowExpressionClauseSyntax)parent).Expression == expression); return true; - case SyntaxKind.SimpleLambdaExpression: - case SyntaxKind.ParenthesizedLambdaExpression: - Debug.Assert(((AnonymousFunctionExpressionSyntax)parent).Body == expression); - return true; - case SyntaxKind.ForStatement: var forStatement = (ForStatementSyntax)parent; return @@ -673,51 +707,9 @@ private static bool IsBreakableExpression(ExpressionSyntax expression) var forEachStatement = (ForEachStatementSyntax)parent; return forEachStatement.Expression == expression; - // Query clauses - case SyntaxKind.FromClause: - var fromClause = (FromClauseSyntax)parent; - - // We can break on the expression in a from clause, except for the first clause in a - // query. For example: - // from c in LookupCustomers() // not here - // from o in LookupBarOrders() + LookupBazOrders() // but here - // group ... into y - // from d in SomeOtherExpression() // and after a continuation, too - - return fromClause.Expression == expression && fromClause.Parent is QueryBodySyntax; - - case SyntaxKind.JoinClause: - var joinClause = (JoinClauseSyntax)parent; - - // We can break on the inner and outer key expressions, but not the - // initializer expression. For example: - // - // join a in alpha /* no */ on beta /* yes */ equals gamma /* yes */ - return joinClause.LeftExpression == expression || joinClause.RightExpression == expression; - - case SyntaxKind.LetClause: - var letClause = (LetClauseSyntax)parent; - return letClause.Expression == expression; - - case SyntaxKind.WhereClause: - var whereClause = (WhereClauseSyntax)parent; - return whereClause.Condition == expression; - - case SyntaxKind.AscendingOrdering: - case SyntaxKind.DescendingOrdering: - var ordering = (OrderingSyntax)parent; - return ordering.Expression == expression; - - case SyntaxKind.SelectClause: - var selectClause = (SelectClauseSyntax)parent; - return selectClause.Expression == expression; - - case SyntaxKind.GroupClause: - var groupClause = (GroupClauseSyntax)parent; - return groupClause.GroupExpression == expression || groupClause.ByExpression == expression; + default: + return LambdaUtilities.IsLambdaBodyStatementOrExpression(expression); } - - return false; } private static TextSpan? CreateSpanForAccessors(SyntaxList accessors, int position) diff --git a/src/Features/CSharp/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index 4be06ec952e51533a0fd463d6d22ccc4d99e18cd..647350b692fc1353e1c56558f444ae19910294a0 100644 --- a/src/Features/CSharp/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -472,9 +472,9 @@ protected override IEnumerable GetLambdaBodyExpressionsAndStatements return SpecializedCollections.SingletonEnumerable(lambdaBody); } - protected override SyntaxNode GetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) + protected override SyntaxNode TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda) { - return LambdaUtilities.GetCorrespondingLambdaBody(oldBody, newLambda); + return LambdaUtilities.TryGetCorrespondingLambdaBody(oldBody, newLambda); } protected override Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit) @@ -587,7 +587,7 @@ protected override bool StatementLabelEquals(SyntaxNode node1, SyntaxNode node2) protected override bool TryGetEnclosingBreakpointSpan(SyntaxNode root, int position, out TextSpan span) { - return root.TryGetClosestBreakpointSpan(position, out span); + return BreakpointSpans.TryGetClosestBreakpointSpan(root, position, out span); } protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out TextSpan span) @@ -607,7 +607,7 @@ protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out // which is lexically not the closest breakpoint span (the body is). // do { ... } [|while (condition);|] var doStatement = (DoStatementSyntax)node; - return node.TryGetClosestBreakpointSpan(doStatement.WhileKeyword.SpanStart, out span); + return BreakpointSpans.TryGetClosestBreakpointSpan(node, doStatement.WhileKeyword.SpanStart, out span); case SyntaxKind.PropertyDeclaration: // The active span corresponding to a property declaration is the span corresponding to its initializer (if any), @@ -616,7 +616,7 @@ protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out var propertyDeclaration = (PropertyDeclarationSyntax)node; if (propertyDeclaration.Initializer != null && - node.TryGetClosestBreakpointSpan(propertyDeclaration.Initializer.SpanStart, out span)) + BreakpointSpans.TryGetClosestBreakpointSpan(node, propertyDeclaration.Initializer.SpanStart, out span)) { return true; } @@ -627,7 +627,7 @@ protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out } default: - return node.TryGetClosestBreakpointSpan(node.SpanStart, out span); + return BreakpointSpans.TryGetClosestBreakpointSpan(node, node.SpanStart, out span); } } @@ -986,18 +986,71 @@ internal override bool QueryClauseLambdasTypeEquivalent(SemanticModel oldModel, case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: + var oldOrderingInfo = oldModel.GetSymbolInfo(oldNode, cancellationToken); + var newOrderingInfo = newModel.GetSymbolInfo(newNode, cancellationToken); + + return MemberSignaturesEquivalent(oldOrderingInfo.Symbol, newOrderingInfo.Symbol); + case SyntaxKind.SelectClause: - case SyntaxKind.GroupClause: - var oldSymbolInfo = oldModel.GetSymbolInfo(oldNode, cancellationToken); - var newSymbolInfo = newModel.GetSymbolInfo(newNode, cancellationToken); + var oldSelectInfo = oldModel.GetSymbolInfo(oldNode, cancellationToken); + var newSelectInfo = newModel.GetSymbolInfo(newNode, cancellationToken); + + // Changing reduced select clause to a non-reduced form or vice versa + // adds/removes a call to Select method, which is a supported change. - return MemberSignaturesEquivalent(oldSymbolInfo.Symbol, newSymbolInfo.Symbol); + return oldSelectInfo.Symbol == null || + newSelectInfo.Symbol == null || + MemberSignaturesEquivalent(oldSelectInfo.Symbol, newSelectInfo.Symbol); + + case SyntaxKind.GroupClause: + var oldGroupByInfo = oldModel.GetSymbolInfo(oldNode, cancellationToken); + var newGroupByInfo = newModel.GetSymbolInfo(newNode, cancellationToken); + return MemberSignaturesEquivalent(oldGroupByInfo.Symbol, newGroupByInfo.Symbol, GroupBySignatureComparer); default: return true; } } + private static bool GroupBySignatureComparer(ImmutableArray oldParameters, ITypeSymbol oldReturnType, ImmutableArray newParameters, ITypeSymbol newReturnType) + { + // C# spec paragraph 7.16.2.6 "Groupby clauses": + // + // A query expression of the form + // from x in e group v by k + // is translated into + // (e).GroupBy(x => k, x => v) + // except when v is the identifier x, the translation is + // (e).GroupBy(x => k) + // + // Possible signatures: + // C> GroupBy(Func keySelector); + // C> GroupBy(Func keySelector, Func elementSelector); + + if (!s_assemblyEqualityComparer.Equals(oldReturnType, newReturnType)) + { + return false; + } + + Debug.Assert(oldParameters.Length == 1 || oldParameters.Length == 2); + Debug.Assert(newParameters.Length == 1 || newParameters.Length == 2); + + // The types of the lambdas have to be the same if present. + // The element selector may be added/removed. + + if (!s_assemblyEqualityComparer.ParameterEquivalenceComparer.Equals(oldParameters[0], newParameters[0])) + { + return false; + } + + if (oldParameters.Length == newParameters.Length && newParameters.Length == 2) + { + return s_assemblyEqualityComparer.ParameterEquivalenceComparer.Equals(oldParameters[1], newParameters[1]); + } + + return true; + } + #endregion #region Diagnostic Info diff --git a/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 2424231438b7c5d81f82a3ae628949766691f937..630d4e5fdfb2013b84d21e494810ca75939db77a 100644 --- a/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -156,7 +156,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i /// protected abstract IEnumerable GetLambdaBodyExpressionsAndStatements(SyntaxNode lambdaBody); - protected abstract SyntaxNode GetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda); + protected abstract SyntaxNode TryGetPartnerLambdaBody(SyntaxNode oldBody, SyntaxNode newLambda); protected abstract Match ComputeTopLevelMatch(SyntaxNode oldCompilationUnit, SyntaxNode newCompilationUnit); protected abstract Match ComputeBodyMatch(SyntaxNode oldBody, SyntaxNode newBody, IEnumerable> knownMatches); @@ -1130,8 +1130,6 @@ internal struct UpdatedMemberInfo if (TryGetLambdaBodies(oldLambda, out oldLambdaBody1, out oldLambdaBody2)) { - Debug.Assert(IsLambda(newLambda)); - if (lambdaBodyMatches == null) { lambdaBodyMatches = ArrayBuilder>.GetInstance(); @@ -1142,11 +1140,19 @@ internal struct UpdatedMemberInfo lazyActiveOrMatchedLambdas = new Dictionary(); } - lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody1, newLambda, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + SyntaxNode newLambdaBody1 = TryGetPartnerLambdaBody(oldLambdaBody1, newLambda); + if (newLambdaBody1 != null) + { + lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody1, newLambdaBody1, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + } if (oldLambdaBody2 != null) { - lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody2, newLambda, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + SyntaxNode newLambdaBody2 = TryGetPartnerLambdaBody(oldLambdaBody2, newLambda); + if (newLambdaBody2 != null) + { + lambdaBodyMatches.Add(ComputeLambdaBodyMatch(oldLambdaBody2, newLambdaBody2, activeNodes, lazyActiveOrMatchedLambdas, diagnostics)); + } } } } @@ -1194,16 +1200,14 @@ internal struct UpdatedMemberInfo private Match ComputeLambdaBodyMatch( SyntaxNode oldLambdaBody, - SyntaxNode newLambda, + SyntaxNode newLambdaBody, ActiveNode[] activeNodes, - [Out]Dictionary lazyActiveOrMatchedLambdas, + [Out]Dictionary activeOrMatchedLambdas, [Out]List diagnostics) { - SyntaxNode newLambdaBody = GetPartnerLambdaBody(oldLambdaBody, newLambda); - ActiveNode[] activeNodesInLambda; LambdaInfo info; - if (lazyActiveOrMatchedLambdas.TryGetValue(oldLambdaBody, out info)) + if (activeOrMatchedLambdas.TryGetValue(oldLambdaBody, out info)) { // Lambda may be matched but not be active. activeNodesInLambda = info.ActiveNodeIndices?.Select(i => activeNodes[i]).ToArray(); @@ -1218,7 +1222,7 @@ internal struct UpdatedMemberInfo bool needsSyntaxMap; var lambdaBodyMatch = ComputeBodyMatch(oldLambdaBody, newLambdaBody, activeNodesInLambda ?? SpecializedCollections.EmptyArray(), diagnostics, out needsSyntaxMap); - lazyActiveOrMatchedLambdas[oldLambdaBody] = info.WithMatch(lambdaBodyMatch, newLambdaBody); + activeOrMatchedLambdas[oldLambdaBody] = info.WithMatch(lambdaBodyMatch, newLambdaBody); return lambdaBodyMatch; } @@ -1948,19 +1952,16 @@ public int GetHashCode(IAssemblySymbol obj) protected static readonly SymbolEquivalenceComparer s_assemblyEqualityComparer = new SymbolEquivalenceComparer(AssemblyEqualityComparer.Instance); - private static bool MethodSignaturesEquivalent(IMethodSymbol oldMethod, IMethodSymbol newMethod) + protected static bool SignaturesEquivalent(ImmutableArray oldParameters, ITypeSymbol oldReturnType, ImmutableArray newParameters, ITypeSymbol newReturnType) { - return oldMethod.Parameters.SequenceEqual(newMethod.Parameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer) && - s_assemblyEqualityComparer.ReturnTypeEquals(oldMethod, newMethod); + return oldParameters.SequenceEqual(newParameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer) && + s_assemblyEqualityComparer.Equals(oldReturnType, newReturnType); } - private static bool PropertySignaturesEquivalent(IPropertySymbol oldProperty, IPropertySymbol newProperty) - { - return oldProperty.Parameters.SequenceEqual(newProperty.Parameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer) && - s_assemblyEqualityComparer.Equals(oldProperty.Type, newProperty.Type); - } - - protected static bool MemberSignaturesEquivalent(ISymbol oldMemberOpt, ISymbol newMemberOpt) + protected static bool MemberSignaturesEquivalent( + ISymbol oldMemberOpt, + ISymbol newMemberOpt, + Func, ITypeSymbol, ImmutableArray, ITypeSymbol, bool> signatureComparer = null) { if (oldMemberOpt == newMemberOpt) { @@ -1972,16 +1973,27 @@ protected static bool MemberSignaturesEquivalent(ISymbol oldMemberOpt, ISymbol n return false; } + if (signatureComparer == null) + { + signatureComparer = SignaturesEquivalent; + } + switch (oldMemberOpt.Kind) { case SymbolKind.Field: - return s_assemblyEqualityComparer.Equals(((IFieldSymbol)oldMemberOpt).Type, ((IFieldSymbol)newMemberOpt).Type); + var oldField = (IFieldSymbol)oldMemberOpt; + var newField = (IFieldSymbol)newMemberOpt; + return signatureComparer(ImmutableArray.Empty, oldField.Type, ImmutableArray.Empty, newField.Type); case SymbolKind.Property: - return PropertySignaturesEquivalent((IPropertySymbol)oldMemberOpt, (IPropertySymbol)newMemberOpt); + var oldProperty = (IPropertySymbol)oldMemberOpt; + var newProperty = (IPropertySymbol)newMemberOpt; + return signatureComparer(oldProperty.Parameters, oldProperty.Type, newProperty.Parameters, newProperty.Type); case SymbolKind.Method: - return MethodSignaturesEquivalent((IMethodSymbol)oldMemberOpt, (IMethodSymbol)newMemberOpt); + var oldMethod = (IMethodSymbol)oldMemberOpt; + var newMethod = (IMethodSymbol)newMemberOpt; + return signatureComparer(oldMethod.Parameters, oldMethod.ReturnType, newMethod.Parameters, newMethod.ReturnType); default: throw ExceptionUtilities.UnexpectedValue(oldMemberOpt.Kind); diff --git a/src/Features/VisualBasic/BasicFeatures.vbproj b/src/Features/VisualBasic/BasicFeatures.vbproj index aaa847e39f7b777e40791c80beb999be0e52d590..c88bce63b0f1d36f136adb3245d9de26db5f5afe 100644 --- a/src/Features/VisualBasic/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/BasicFeatures.vbproj @@ -343,6 +343,7 @@ + diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/BreakpointSpans.vb b/src/Features/VisualBasic/EditAndContinue/BreakpointSpans.vb similarity index 93% rename from src/Workspaces/VisualBasic/Portable/Extensions/BreakpointSpans.vb rename to src/Features/VisualBasic/EditAndContinue/BreakpointSpans.vb index 73a51f9db2972e46d0d4655e6b2194d8996f7f90..1f8849d951490d22c4fcecec7413fc4e37c9713f 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/BreakpointSpans.vb +++ b/src/Features/VisualBasic/EditAndContinue/BreakpointSpans.vb @@ -1,17 +1,46 @@ ' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -Imports System -Imports System.Linq Imports System.Runtime.InteropServices -Imports System.Threading Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.Text -Imports System.Runtime.CompilerServices +Imports System.Threading -Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions - ' TODO (tomat): move to the compiler. Bug #764678. +Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Friend Module BreakpointSpans + Friend Function TryGetBreakpointSpan(tree As SyntaxTree, position As Integer, cancellationToken As CancellationToken, ByRef breakpointSpan As TextSpan) As Boolean + Dim source = tree.GetText(cancellationToken) + + ' If the line is entirely whitespace, then don't set any breakpoint there. + Dim line = source.Lines.GetLineFromPosition(position) + If IsBlank(line) Then + breakpointSpan = Nothing + Return False + End If + + ' If the user is asking for breakpoint in an inactive region, then just create a line + ' breakpoint there. + If tree.IsInInactiveRegion(position, cancellationToken) Then + breakpointSpan = Nothing + Return True + End If + + Dim root = tree.GetRoot(cancellationToken) + Return TryGetEnclosingBreakpointSpan(root, position, breakpointSpan) + End Function + + Private Function IsBlank(line As TextLine) As Boolean + Dim text = line.ToString() + + For i = 0 To text.Length - 1 + If Not SyntaxFacts.IsWhitespace(text(i)) Then + Return False + End If + Next + + Return True + End Function + ''' ''' Given a syntax token determines a text span delimited by the closest applicable sequence points ''' encompassing the token. @@ -19,7 +48,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions ''' ''' If the span exists it Is possible To place a breakpoint at the given position. ''' - Public Function TryGetEnclosingBreakpointSpan(root As SyntaxNode, position As Integer, ByRef span As TextSpan) As Boolean Dim node = root.FindToken(position).Parent diff --git a/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 7a37bcae2c37a0b348ff856961eca6a2a279a132..e0f18bea74048590cc9caaa09d9e07f44b118cb0 100644 --- a/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -555,7 +555,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Nothing End Function - Protected Overrides Function GetPartnerLambdaBody(oldBody As SyntaxNode, newLambda As SyntaxNode) As SyntaxNode + Protected Overrides Function TryGetPartnerLambdaBody(oldBody As SyntaxNode, newLambda As SyntaxNode) As SyntaxNode Return LambdaUtilities.GetCorrespondingLambdaBody(oldBody, newLambda) End Function @@ -726,11 +726,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Function Protected Overrides Function TryGetEnclosingBreakpointSpan(root As SyntaxNode, position As Integer, ByRef span As TextSpan) As Boolean - Return root.TryGetEnclosingBreakpointSpan(position, span) + Return BreakpointSpans.TryGetEnclosingBreakpointSpan(root, position, span) End Function Protected Overrides Function TryGetActiveSpan(node As SyntaxNode, statementPart As Integer, ByRef span As TextSpan) As Boolean - Return node.TryGetEnclosingBreakpointSpan(node.SpanStart, span) + Return BreakpointSpans.TryGetEnclosingBreakpointSpan(node, node.SpanStart, span) End Function Protected Overrides Iterator Function EnumerateNearStatements(statement As SyntaxNode) As IEnumerable(Of KeyValuePair(Of SyntaxNode, Integer)) diff --git a/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj index af538b9b4b15c14754a488a8ca9003bfe19cd987..b20b845286d88f9f1b93003d40b4701a8fe1fc9c 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj +++ b/src/VisualStudio/CSharp/Impl/CSharpVisualStudio.csproj @@ -180,7 +180,6 @@ - diff --git a/src/VisualStudio/CSharp/Impl/Debugging/BreakpointGetter.cs b/src/VisualStudio/CSharp/Impl/Debugging/BreakpointGetter.cs deleted file mode 100644 index 685e3e5b2e602f3312ee9effbace9e523e0417a9..0000000000000000000000000000000000000000 --- a/src/VisualStudio/CSharp/Impl/Debugging/BreakpointGetter.cs +++ /dev/null @@ -1,72 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.Editor.Implementation.Debugging; -using Microsoft.CodeAnalysis.Text; - -namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging -{ - internal static class BreakpointGetter - { - internal static async Task GetBreakpointAsync(Document document, int position, CancellationToken cancellationToken) - { - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); - - TextSpan span; - if (!TryGetBreakpointSpan(tree, position, cancellationToken, out span)) - { - return null; - } - - if (span.Length == 0) - { - return BreakpointResolutionResult.CreateLineResult(document); - } - - return BreakpointResolutionResult.CreateSpanResult(document, span); - } - - internal static bool TryGetBreakpointSpan(SyntaxTree tree, int position, CancellationToken cancellationToken, out TextSpan breakpointSpan) - { - var source = tree.GetText(cancellationToken); - - // If the line is entirely whitespace, then don't set any breakpoint there. - var line = source.Lines.GetLineFromPosition(position); - if (IsBlank(line)) - { - breakpointSpan = default(TextSpan); - return false; - } - - // If the user is asking for breakpoint in an inactive region, then just create a line - // breakpoint there. - if (tree.IsInInactiveRegion(position, cancellationToken)) - { - breakpointSpan = default(TextSpan); - return true; - } - - var root = tree.GetRoot(cancellationToken); - return root.TryGetClosestBreakpointSpan(position, out breakpointSpan); - } - - private static bool IsBlank(TextLine line) - { - var text = line.ToString(); - - for (int i = 0; i < text.Length; i++) - { - if (!SyntaxFacts.IsWhitespace(text[i])) - { - return false; - } - } - - return true; - } - } -} diff --git a/src/VisualStudio/CSharp/Impl/Debugging/CSharpBreakpointResolutionService.cs b/src/VisualStudio/CSharp/Impl/Debugging/CSharpBreakpointResolutionService.cs index bd08961fa54c2251c819f6cf1e2516cf3d73e57e..6e5fbfd9cad83580b462c2e4a617ca2bd83b7277 100644 --- a/src/VisualStudio/CSharp/Impl/Debugging/CSharpBreakpointResolutionService.cs +++ b/src/VisualStudio/CSharp/Impl/Debugging/CSharpBreakpointResolutionService.cs @@ -5,6 +5,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.EditAndContinue; using Microsoft.CodeAnalysis.Editor.Implementation.Debugging; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; @@ -14,10 +15,28 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.Debugging [ExportLanguageService(typeof(IBreakpointResolutionService), LanguageNames.CSharp), Shared] internal partial class CSharpBreakpointResolutionService : IBreakpointResolutionService { + internal static async Task GetBreakpointAsync(Document document, int position, CancellationToken cancellationToken) + { + var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + + TextSpan span; + if (!BreakpointSpans.TryGetBreakpointSpan(tree, position, cancellationToken, out span)) + { + return null; + } + + if (span.Length == 0) + { + return BreakpointResolutionResult.CreateLineResult(document); + } + + return BreakpointResolutionResult.CreateSpanResult(document, span); + } + public Task ResolveBreakpointAsync( Document document, TextSpan textSpan, CancellationToken cancellationToken) { - return BreakpointGetter.GetBreakpointAsync(document, textSpan.Start, cancellationToken); + return GetBreakpointAsync(document, textSpan.Start, cancellationToken); } public Task> ResolveBreakpointsAsync(Solution solution, string name, CancellationToken cancellationToken) diff --git a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj index 86691cb1bea527f3ed7203bd3bfdf1bc1056582e..4cc8f548dda6a06ddec4b470418c6d713eb83ec4 100644 --- a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj +++ b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj @@ -160,7 +160,6 @@ - diff --git a/src/VisualStudio/Core/Test/Debugging/VisualBasicBreakpointResolutionServiceTests.vb b/src/VisualStudio/Core/Test/Debugging/VisualBasicBreakpointResolutionServiceTests.vb new file mode 100644 index 0000000000000000000000000000000000000000..37dfd0c1d742e648082a6e4135c92d96c8d65f0b --- /dev/null +++ b/src/VisualStudio/Core/Test/Debugging/VisualBasicBreakpointResolutionServiceTests.vb @@ -0,0 +1,59 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Threading +Imports System.Xml.Linq +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Editor.Implementation.Debugging +Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions +Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging +Imports Roslyn.Test.Utilities +Imports Roslyn.Utilities + +Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.UnitTests.Debugging + Public Class VisualBasicBreakpointResolutionServiceTests + + Public Sub TestSpanWithLength(markup As XElement, length As Integer) + Dim position As Integer? = Nothing + Dim expectedSpan As TextSpan? = Nothing + Dim source As String = Nothing + MarkupTestFile.GetPositionAndSpan(markup.NormalizedValue, source, position, expectedSpan) + + Using workspace = VisualBasicWorkspaceFactory.CreateWorkspaceFromLines(source) + Dim document = workspace.CurrentSolution.Projects.First.Documents.First + Dim result As BreakpointResolutionResult = VisualBasicBreakpointResolutionService.GetBreakpointAsync(document, position.Value, length, CancellationToken.None).WaitAndGetResult(CancellationToken.None) + Assert.True(expectedSpan.Value = result.TextSpan, + String.Format(vbCrLf & "Expected: {0} ""{1}""" & vbCrLf & "Actual: {2} ""{3}""", + expectedSpan.Value, + source.Substring(expectedSpan.Value.Start, expectedSpan.Value.Length), + result.TextSpan, + source.Substring(result.TextSpan.Start, result.TextSpan.Length))) + End Using + End Sub + + + + Public Sub TestBreakpointSpansMultipleMethods() + ' Normal case: debugger passing BP spans "sub Foo() end sub" + TestSpanWithLength( +class C + [|$$sub Foo()|] + end sub + + sub Bar() + end sub +end class, 20) + + ' Rare case: debugger passing BP spans "sub Foo() end sub sub Bar() end sub" + TestSpanWithLength( +class C + $$sub Foo() + end sub + + [|sub Bar()|] + end sub +end class, 35) + End Sub + End Class +End Namespace diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index 0dd0c7b8e9edd0cd187ff8f08621c57e45639068..68564e6cd35bdcdf287e7e7bf48f71a532005396 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -313,7 +313,6 @@ - @@ -325,6 +324,7 @@ True Resources.resx + diff --git a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj index 53019e399ef0a943e77120f79e4861c6f309ac07..f9aca3e807fe504ad6bd09d2f9f06d95734071c1 100644 --- a/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj +++ b/src/VisualStudio/VisualBasic/Impl/BasicVisualStudio.vbproj @@ -107,7 +107,6 @@ - diff --git a/src/VisualStudio/VisualBasic/Impl/Debugging/BreakpointGetter.vb b/src/VisualStudio/VisualBasic/Impl/Debugging/BreakpointGetter.vb deleted file mode 100644 index 6a7bbff5a84805c1bba676ce0401924721bb0a8d..0000000000000000000000000000000000000000 --- a/src/VisualStudio/VisualBasic/Impl/Debugging/BreakpointGetter.vb +++ /dev/null @@ -1,92 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Threading -Imports Microsoft.CodeAnalysis -Imports Microsoft.CodeAnalysis.Text -Imports Microsoft.CodeAnalysis.VisualBasic -Imports Microsoft.CodeAnalysis.VisualBasic.Extensions -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax -Imports System.Runtime.InteropServices -Imports System.Threading.Tasks -Imports Microsoft.CodeAnalysis.Editor.Implementation.Debugging - -Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging - Friend Module BreakpointGetter - Friend Async Function GetBreakpointAsync(document As Document, position As Integer, length As Integer, cancellationToken As CancellationToken) As Task(Of BreakpointResolutionResult) - Dim tree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False) - - ' Non-zero length means that the span is passed by the debugger and we may need validate it. - ' In a rare VB case, this span may contain multiple methods, e.g., - ' - ' [Sub Foo() Handles A - ' - ' End Sub - ' - ' Sub Bar() Handles B] - ' - ' End Sub - ' - ' It happens when IDE services (e.g., NavBar or CodeFix) inserts a new method at the beginning of the existing one - ' which happens to have a breakpoint on its head. In this situation, we should attempt to validate the span of the existing method, - ' not that of a newly-prepended method. - - Dim descendIntoChildren As Func(Of SyntaxNode, Boolean) = - Function(n) - Return (Not n.IsKind(SyntaxKind.ConstructorBlock)) _ - AndAlso (Not n.IsKind(SyntaxKind.SubBlock)) - End Function - - If length > 0 Then - Dim root = Await tree.GetRootAsync(cancellationToken).ConfigureAwait(False) - Dim item = root.DescendantNodes(New TextSpan(position, length), descendIntoChildren:=descendIntoChildren).OfType(Of MethodBlockSyntax).Skip(1).LastOrDefault() - If item IsNot Nothing Then - position = item.SpanStart - End If - End If - - Dim span As TextSpan - If Not TryGetBreakpointSpan(tree, position, cancellationToken, span) Then - Return Nothing - End If - - If span.Length = 0 Then - Return BreakpointResolutionResult.CreateLineResult(document) - End If - - Return BreakpointResolutionResult.CreateSpanResult(document, span) - End Function - - Friend Function TryGetBreakpointSpan(tree As SyntaxTree, position As Integer, cancellationToken As CancellationToken, ByRef breakpointSpan As TextSpan) As Boolean - Dim source = tree.GetText(cancellationToken) - - ' If the line is entirely whitespace, then don't set any breakpoint there. - Dim line = source.Lines.GetLineFromPosition(position) - If IsBlank(line) Then - breakpointSpan = Nothing - Return False - End If - - ' If the user is asking for breakpoint in an inactive region, then just create a line - ' breakpoint there. - If tree.IsInInactiveRegion(position, cancellationToken) Then - breakpointSpan = Nothing - Return True - End If - - Dim root = tree.GetRoot(cancellationToken) - Return root.TryGetEnclosingBreakpointSpan(position, breakpointSpan) - End Function - - Private Function IsBlank(line As TextLine) As Boolean - Dim text = line.ToString() - - For i = 0 To text.Length - 1 - If Not SyntaxFacts.IsWhitespace(text(i)) Then - Return False - End If - Next - - Return True - End Function - End Module -End Namespace diff --git a/src/VisualStudio/VisualBasic/Impl/Debugging/VisualBasicBreakpointService.vb b/src/VisualStudio/VisualBasic/Impl/Debugging/VisualBasicBreakpointService.vb index eb026d46ac2c9b4571da1564ce57790ebd91c245..8d8b4dcf239a792433ab08a2aa921ed795249445 100644 --- a/src/VisualStudio/VisualBasic/Impl/Debugging/VisualBasicBreakpointService.vb +++ b/src/VisualStudio/VisualBasic/Impl/Debugging/VisualBasicBreakpointService.vb @@ -7,6 +7,9 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Editor.Implementation.Debugging Imports Microsoft.CodeAnalysis.Host.Mef Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.EditAndContinue +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging @@ -14,8 +17,52 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Debugging Partial Friend Class VisualBasicBreakpointResolutionService Implements IBreakpointResolutionService + Friend Shared Async Function GetBreakpointAsync(document As Document, position As Integer, length As Integer, cancellationToken As CancellationToken) As Task(Of BreakpointResolutionResult) + Dim tree = Await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(False) + + ' Non-zero length means that the span is passed by the debugger and we may need validate it. + ' In a rare VB case, this span may contain multiple methods, e.g., + ' + ' [Sub Foo() Handles A + ' + ' End Sub + ' + ' Sub Bar() Handles B] + ' + ' End Sub + ' + ' It happens when IDE services (e.g., NavBar or CodeFix) inserts a new method at the beginning of the existing one + ' which happens to have a breakpoint on its head. In this situation, we should attempt to validate the span of the existing method, + ' not that of a newly-prepended method. + + Dim descendIntoChildren As Func(Of SyntaxNode, Boolean) = + Function(n) + Return (Not n.IsKind(SyntaxKind.ConstructorBlock)) _ + AndAlso (Not n.IsKind(SyntaxKind.SubBlock)) + End Function + + If length > 0 Then + Dim root = Await tree.GetRootAsync(cancellationToken).ConfigureAwait(False) + Dim item = root.DescendantNodes(New TextSpan(position, length), descendIntoChildren:=descendIntoChildren).OfType(Of MethodBlockSyntax).Skip(1).LastOrDefault() + If item IsNot Nothing Then + position = item.SpanStart + End If + End If + + Dim span As TextSpan + If Not BreakpointSpans.TryGetBreakpointSpan(tree, position, cancellationToken, span) Then + Return Nothing + End If + + If span.Length = 0 Then + Return BreakpointResolutionResult.CreateLineResult(document) + End If + + Return BreakpointResolutionResult.CreateSpanResult(document, span) + End Function + Public Function ResolveBreakpointAsync(document As Document, textSpan As TextSpan, Optional cancellationToken As CancellationToken = Nothing) As Task(Of BreakpointResolutionResult) Implements IBreakpointResolutionService.ResolveBreakpointAsync - Return BreakpointGetter.GetBreakpointAsync(document, textSpan.Start, textSpan.Length, cancellationToken) + Return GetBreakpointAsync(document, textSpan.Start, textSpan.Length, cancellationToken) End Function Public Function ResolveBreakpointsAsync(solution As Solution, diff --git a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj index aaf4b2a797ffb62f4b7e61de32bf8a55844ab875..11beb03731f1707b6327908987da5184b9a8b639 100644 --- a/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj +++ b/src/Workspaces/CSharp/Portable/CSharpWorkspace.csproj @@ -126,7 +126,6 @@ - @@ -268,4 +267,4 @@ - + \ No newline at end of file diff --git a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj index 89e2ebb19e5221768c532e5fe0d1b2b60fa1af98..c6c80866c4ebb38b3ea9fc93783f3e94e5375197 100644 --- a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj +++ b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj @@ -137,7 +137,6 @@ -