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

Merge pull request #676 from tmat/LambdaMatching

EnC: Calculate reverse map for each updated method containing lambdas
......@@ -371,12 +371,7 @@ internal static bool IsStatementExpression(CSharpSyntaxNode syntax)
/// </summary>
public static bool IsLambdaBody(SyntaxNode node)
{
if (node == null)
{
return false;
}
var parent = node.Parent;
var parent = node?.Parent;
if (parent == null)
{
return false;
......
......@@ -23,19 +23,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Case SyntaxKind.WhereClause
Return DirectCast(newLambda, WhereClauseSyntax).Condition
' source sequence in From and Aggregate (other than the first in the query)
Case SyntaxKind.CollectionRangeVariable
Return DirectCast(newLambda, CollectionRangeVariableSyntax).Expression
' function call in Group By, Group Join, Aggregate: the argument
Case SyntaxKind.FunctionAggregation
Return DirectCast(newLambda, FunctionAggregationSyntax).Argument
' variable in Let, Select, Group By: the RHS
Case SyntaxKind.ExpressionRangeVariable
Return DirectCast(newLambda, ExpressionRangeVariableSyntax).Expression
Case SyntaxKind.TakeWhileClause, SyntaxKind.SkipWhileClause
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Return DirectCast(newLambda, PartitionWhileClauseSyntax).Condition
Case SyntaxKind.AscendingOrdering, SyntaxKind.DescendingOrdering
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Return DirectCast(newLambda, OrderingSyntax).Expression
Case SyntaxKind.JoinCondition
......
......@@ -6178,6 +6178,42 @@ static void Main(string[] args)
Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "b", "method"));
}
[Fact]
public void Lambdas_ActiveStatementRemoved4()
{
string src1 = @"
class C
{
static void Main(string[] args)
{
Func<int, Func<int, int>> f = a =>
{
<AS:1>z(2);</AS:1>
return b =>
{
<AS:0>return b;</AS:0>
};
};
}
}";
string src2 = @"
class C
{
static void Main(string[] args)
<AS:0,1>{</AS:0,1>
}
}
";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "{", "lambda"),
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "{", "lambda"));
}
[Fact]
public void Queries_ActiveStatementRemoved_WhereClause()
{
......
......@@ -88,51 +88,48 @@ internal static Match<SyntaxNode> GetMethodMatch(string src1, string src2, Parse
return match;
}
internal static IEnumerable<Match<SyntaxNode>> GetMethodMatches(string src1, string src2, ParseOptions options = null, StateMachineKind stateMachine = StateMachineKind.None)
internal static IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> GetMethodMatches(string src1, string src2, ParseOptions options = null, StateMachineKind stateMachine = StateMachineKind.None)
{
var methodMatch = GetMethodMatch(src1, src2, options, stateMachine);
var queue = new Queue<Match<SyntaxNode>>();
queue.Enqueue(methodMatch);
bool hasLambda;
Dictionary<SyntaxNode, AbstractEditAndContinueAnalyzer.LambdaInfo> lazyActiveOrMatchedLambdas = null;
var reverseMap = Analyzer.ComputeReverseMap(methodMatch, new AbstractEditAndContinueAnalyzer.ActiveNode[0], ref lazyActiveOrMatchedLambdas, new List<RudeEditDiagnostic>(), out hasLambda);
while (queue.Count > 0)
var result = new Dictionary<SyntaxNode, SyntaxNode>();
foreach (var pair in reverseMap)
{
var match = queue.Dequeue();
yield return match;
foreach (var m in match.Matches)
if (pair.Key == methodMatch.NewRoot)
{
if (m.Key == match.OldRoot)
{
Assert.Equal(match.NewRoot, m.Value);
continue;
}
foreach (var body in GetLambdaBodies(m.Key, m.Value))
{
var lambdaMatch = new StatementSyntaxComparer(body.Item1, body.Item2).ComputeMatch(m.Key, m.Value);
queue.Enqueue(lambdaMatch);
}
Assert.Same(pair.Value, methodMatch.OldRoot);
continue;
}
result.Add(pair.Value, pair.Key);
}
return result;
}
public static MatchingPairs ToMatchingPairs(Match<SyntaxNode> match)
{
return new MatchingPairs(ToMatchingPairs(match.Matches.Where(partners => partners.Key != match.OldRoot)));
return ToMatchingPairs(match.Matches.Where(partners => partners.Key != match.OldRoot));
}
public static MatchingPairs ToMatchingPairs(IEnumerable<Match<SyntaxNode>> match)
public static MatchingPairs ToMatchingPairs(IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> matches)
{
return new MatchingPairs(ToMatchingPairs(match.SelectMany(m => m.Matches.Where(partners => partners.Key != m.OldRoot))));
return new MatchingPairs(matches
.OrderBy(partners => partners.Key.GetLocation().SourceSpan.Start)
.ThenByDescending(partners => partners.Key.Span.Length)
.Select(partners => new MatchingPair { Old = partners.Key.ToString().Replace("\r\n", " "), New = partners.Value.ToString().Replace("\r\n", " ") }));
}
private static IEnumerable<MatchingPair> ToMatchingPairs(IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> matches)
private static IEnumerable<KeyValuePair<K, V>> ReverseMapping<K,V>(IEnumerable<KeyValuePair<V, K>> mapping)
{
return matches
.OrderBy(partners => partners.Key.GetLocation().SourceSpan.Start)
.ThenByDescending(partners => partners.Key.Span.Length)
.Select(partners => new MatchingPair { Old = partners.Key.ToString().Replace("\r\n", " "), New = partners.Value.ToString().Replace("\r\n", " ") });
foreach (var pair in mapping)
{
yield return KeyValuePair.Create(pair.Value, pair.Key);
}
}
internal static BlockSyntax MakeMethodBody(
......@@ -183,46 +180,6 @@ internal static SyntaxMapDescription GetSyntaxMap(string oldSource, string newSo
return new SyntaxMapDescription(oldSource, newSource);
}
private static ImmutableArray<ValueTuple<SyntaxNode, SyntaxNode>> GetLambdaBodies(SyntaxNode oldNode, SyntaxNode newNode)
{
switch (oldNode.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((AnonymousFunctionExpressionSyntax)oldNode).Body, ((AnonymousFunctionExpressionSyntax)newNode).Body));
case SyntaxKind.FromClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((FromClauseSyntax)oldNode).Expression, ((FromClauseSyntax)newNode).Expression));
case SyntaxKind.LetClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((LetClauseSyntax)oldNode).Expression, ((LetClauseSyntax)newNode).Expression));
case SyntaxKind.WhereClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((WhereClauseSyntax)oldNode).Condition, ((WhereClauseSyntax)newNode).Condition));
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((OrderingSyntax)oldNode).Expression, ((OrderingSyntax)newNode).Expression));
case SyntaxKind.SelectClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((SelectClauseSyntax)oldNode).Expression, ((SelectClauseSyntax)newNode).Expression));
case SyntaxKind.JoinClause:
return ImmutableArray.Create(
ValueTuple.Create<SyntaxNode, SyntaxNode>(((JoinClauseSyntax)oldNode).LeftExpression, ((JoinClauseSyntax)newNode).LeftExpression),
ValueTuple.Create<SyntaxNode, SyntaxNode>(((JoinClauseSyntax)oldNode).RightExpression, ((JoinClauseSyntax)newNode).RightExpression));
case SyntaxKind.GroupClause:
return ImmutableArray.Create(
ValueTuple.Create<SyntaxNode, SyntaxNode>(((GroupClauseSyntax)oldNode).GroupExpression, ((GroupClauseSyntax)newNode).ByExpression),
ValueTuple.Create<SyntaxNode, SyntaxNode>(((GroupClauseSyntax)oldNode).GroupExpression, ((GroupClauseSyntax)newNode).ByExpression));
default:
return ImmutableArray<ValueTuple<SyntaxNode, SyntaxNode>>.Empty;
}
}
internal static void VerifyPreserveLocalVariables(EditScript<SyntaxNode> edits, bool preserveLocalVariables)
{
var decl1 = (MethodDeclarationSyntax)((ClassDeclarationSyntax)((CompilationUnitSyntax)edits.Match.OldRoot).Members[0]).Members[0];
......
......@@ -1816,6 +1816,34 @@ class C
new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Main"), preserveLocalVariables: false) });
}
[Fact(Skip = "TODO: Enable lambda edits")]
public void MethodWithLambda_Update()
{
string src1 = @"
class C
{
static void F()
{
Func<int> a = () => 1;
}
}
";
string src2 = @"
class C
{
static void F()
{
Func<int> a = () => 1;
Console.WriteLine(1);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true) });
}
[Fact]
public void MethodUpdate_LocalVariableDeclaration()
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests
{
......@@ -45,23 +47,23 @@ internal static string ClearTags(string source)
}
private static readonly Regex s_tags = new Regex(
@"[<][/]?(AS|ER|N|TS)[:][.0-9]+[>]",
@"[<][/]?(AS|ER|N|TS)[:][.0-9,]+[>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
private static readonly Regex s_activeStatementPattern = new Regex(
@"[<]AS[:] (?<Id>[0-9]+) [>]
@"[<]AS[:] (?<Id>[0-9,]+) [>]
(?<ActiveStatement>.*)
[<][/]AS[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
public static readonly Regex ExceptionRegionPattern = new Regex(
@"[<]ER[:] (?<Id>[0-9]+[.][0-9]+) [>]
@"[<]ER[:] (?<Id>(?:[0-9]+[.][0-9]+[,]?)+) [>]
(?<ExceptionRegion>.*)
[<][/]ER[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
private static readonly Regex s_trackingStatementPattern = new Regex(
@"[<]TS[:] (?<Id>[0-9]+) [>]
@"[<]TS[:] (?<Id>[0-9,]+) [>]
(?<TrackingStatement>.*)
[<][/]TS[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
......@@ -84,6 +86,18 @@ internal static ActiveStatementSpan[] GetActiveStatements(string src)
return result.ToArray();
}
internal static IEnumerable<int> GetIds(Match match)
{
return match.Groups["Id"].Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
}
internal static IEnumerable<ValueTuple<int, int>> GetDottedIds(Match match)
{
return from ids in match.Groups["Id"].Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
let parts = ids.Split('.')
select ValueTuple.Create(int.Parse(parts[0]), int.Parse(parts[1]));
}
internal static TextSpan[] GetActiveSpans(string src)
{
var matches = s_activeStatementPattern.Matches(src);
......@@ -92,10 +106,11 @@ internal static TextSpan[] GetActiveSpans(string src)
for (int i = 0; i < matches.Count; i++)
{
var stmt = matches[i].Groups["ActiveStatement"];
var id = int.Parse(matches[i].Groups["Id"].Value);
EnsureSlot(result, id);
result[id] = new TextSpan(stmt.Index, stmt.Length);
foreach (int id in GetIds(matches[i]))
{
EnsureSlot(result, id);
result[id] = new TextSpan(stmt.Index, stmt.Length);
}
}
return result.ToArray();
......@@ -114,8 +129,10 @@ internal static TextSpan[] GetActiveSpans(string src)
for (int i = 0; i < matches.Count; i++)
{
var span = matches[i].Groups["TrackingStatement"];
var id = int.Parse(matches[i].Groups["Id"].Value);
result[id] = new TextSpan(span.Index, span.Length);
foreach (int id in GetIds(matches[i]))
{
result[id] = new TextSpan(span.Index, span.Length);
}
}
return result;
......@@ -128,18 +145,21 @@ internal static ImmutableArray<TextSpan>[] GetExceptionRegions(string src, int a
for (int i = 0; i < matches.Count; i++)
{
var stmt = matches[i].Groups["ExceptionRegion"];
var id = matches[i].Groups["Id"].Value.Split('.');
var asid = int.Parse(id[0]);
var erid = int.Parse(id[1]);
var exceptionRegion = matches[i].Groups["ExceptionRegion"];
if (result[asid] == null)
foreach (var id in GetDottedIds(matches[i]))
{
result[asid] = new List<TextSpan>();
}
var activeStatementId = id.Item1;
var exceptionRegionId = id.Item2;
if (result[activeStatementId] == null)
{
result[activeStatementId] = new List<TextSpan>();
}
EnsureSlot(result[asid], erid);
result[asid][erid] = new TextSpan(stmt.Index, stmt.Length);
EnsureSlot(result[activeStatementId], exceptionRegionId);
result[activeStatementId][exceptionRegionId] = new TextSpan(exceptionRegion.Index, exceptionRegion.Length);
}
}
return result.Select(r => r.AsImmutableOrEmpty()).ToArray();
......
......@@ -93,7 +93,7 @@ internal abstract class EditAndContinueTestHelpers
var diagnostics = new List<RudeEditDiagnostic>();
var actualNewActiveStatements = new LinePositionSpan[oldActiveStatements.Length];
var actualNewExceptionRegions = new ImmutableArray<LinePositionSpan>[oldActiveStatements.Length];
var updatedActiveMethodMatches = new List<ValueTuple<int, Match<SyntaxNode>>>();
var updatedActiveMethodMatches = new List<ValueTuple<int, IReadOnlyDictionary<SyntaxNode, SyntaxNode>>>();
var editMap = Analyzer.BuildEditMap(editScript);
DocumentId documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument");
......@@ -239,7 +239,7 @@ internal abstract class EditAndContinueTestHelpers
var newModel = newCompilation.GetSemanticModel(newRoot.SyntaxTree);
var oldActiveStatements = activeStatements.OldSpans.AsImmutable();
var updatedActiveMethodMatches = new List<ValueTuple<int, Match<SyntaxNode>>>();
var updatedActiveMethodMatches = new List<ValueTuple<int, IReadOnlyDictionary<SyntaxNode, SyntaxNode>>>();
var triviaEdits = new List<KeyValuePair<SyntaxNode, SyntaxNode>>();
var actualLineEdits = new List<LineChange>();
var actualSemanticEdits = new List<SemanticEdit>();
......
......@@ -4166,6 +4166,34 @@ End Class
Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "Function(b)", "method"))
End Sub
<Fact>
Public Sub Lambdas_ActiveStatementRemoved4()
Dim src1 = "
Class C
Shared Sub Main()
Dim f = Function(a)
<AS:1>z(2)</AS:1>
return Function (b)
<AS:0>Return b</AS:0>
End Function
End Function
End Sub
End Class"
Dim src2 = "
Class C
<AS:0,1>Shared Sub Main()</AS:0,1>
End Sub
End Class
"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "Shared Sub Main()", "lambda"),
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "Shared Sub Main()", "lambda"))
End Sub
<Fact>
Public Sub Queries_ActiveStatementRemoved_WhereClause()
Dim src1 = "
......
......@@ -2001,6 +2001,30 @@ End Class
Diagnostic(RudeEditKind.AwaitStatementUpdate, "a += Await F(1)"))
End Sub
<Fact(Skip:="TODO: Enable lambda edits")>
Public Sub MethodWithLambda_Update()
Dim src1 = "
Class C
Shared Sub F()
Dim a = Function() 1
End Sub
End Class
"
Dim src2 = "
Class C
Shared Sub F()
Dim a = Function() 1
Console.WriteLine(1)
End Sub
End Class
"
Dim edits = GetTopEdits(src1, src2)
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
{SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True)})
End Sub
<Fact>
Public Sub MethodUpdate_AddAttribute()
Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class"
......
......@@ -839,6 +839,21 @@ private static bool IsGetterToExpressionBodyTransformation(EditKind editKind, Sy
return false;
}
internal override bool ContainsLambda(SyntaxNode declaration)
{
return declaration.DescendantNodes().Any(SyntaxUtilities.IsLambda);
}
internal override bool IsLambda(SyntaxNode node)
{
return SyntaxUtilities.IsLambda(node);
}
internal override bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2)
{
return SyntaxUtilities.TryGetLambdaBodies(node, out body1, out body2);
}
#endregion
#region Diagnostic Info
......
......@@ -162,6 +162,11 @@ public static bool IsNotLambda(SyntaxNode node)
return !IsLambda(node.Kind());
}
public static bool IsLambda(SyntaxNode node)
{
return IsLambda(node.Kind());
}
public static bool IsLambda(SyntaxKind kind)
{
switch (kind)
......@@ -187,6 +192,62 @@ public static bool IsLambda(SyntaxKind kind)
return false;
}
public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2)
{
body1 = null;
body2 = null;
switch (node.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
body1 = ((AnonymousFunctionExpressionSyntax)node).Body;
return true;
case SyntaxKind.FromClause:
var fromClause = (FromClauseSyntax)node;
if (!(fromClause.Parent is QueryBodySyntax))
{
return false;
}
body1 = fromClause.Expression;
return true;
case SyntaxKind.JoinClause:
var joinClause = (JoinClauseSyntax)node;
body1 = joinClause.LeftExpression;
body2 = joinClause.RightExpression;
return true;
case SyntaxKind.LetClause:
body1 = ((LetClauseSyntax)node).Expression;
return true;
case SyntaxKind.WhereClause:
body1 = ((WhereClauseSyntax)node).Condition;
return true;
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
body1 = ((OrderingSyntax)node).Expression;
return true;
case SyntaxKind.SelectClause:
body1 = ((SelectClauseSyntax)node).Expression;
return true;
case SyntaxKind.GroupClause:
var groupClause = (GroupClauseSyntax)node;
body1 = groupClause.GroupExpression;
body2 = groupClause.ByExpression;
return true;
}
return false;
}
public static bool Any(TypeParameterListSyntax listOpt)
{
return listOpt != null && listOpt.ChildNodesAndTokens().Count != 0;
......@@ -279,31 +340,10 @@ public static bool HasBackingField(PropertyDeclarationSyntax property)
public static bool IsAsyncMethodOrLambda(SyntaxNode declaration)
{
if (declaration.IsKind(SyntaxKind.ParenthesizedLambdaExpression))
{
var lambda = (ParenthesizedLambdaExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
}
if (declaration.IsKind(SyntaxKind.SimpleLambdaExpression))
{
var lambda = (SimpleLambdaExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
}
if (declaration.IsKind(SyntaxKind.AnonymousMethodExpression))
var anonymousFunction = declaration as AnonymousFunctionExpressionSyntax;
if (anonymousFunction != null)
{
var lambda = (AnonymousMethodExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
return anonymousFunction.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword);
}
// expression bodied methods:
......
' 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.Collections.Immutable
Imports System.Composition
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Differencing
Imports Microsoft.CodeAnalysis.EditAndContinue
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
......@@ -16,8 +9,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
<Conditional("DEBUG")>
Public Shared Sub AssertIsBody(syntax As SyntaxNode, allowLambda As Boolean)
' lambda/query
Dim body As SyntaxNode = Nothing
If IsLambdaBodyStatement(syntax, body) AndAlso syntax Is body Then
If IsLambdaBody(syntax) Then
Debug.Assert(allowLambda)
Debug.Assert(TypeOf syntax Is ExpressionSyntax OrElse TypeOf syntax Is LambdaHeaderSyntax)
Return
......@@ -60,58 +52,85 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Debug.Assert(False)
End Sub
Public Shared Function IsLambdaBodyStatement(node As SyntaxNode, <Out> ByRef body As SyntaxNode) As Boolean
Dim parent = node.Parent
Public Shared Function IsLambdaBody(node As SyntaxNode) As Boolean
Dim body As SyntaxNode = Nothing
Return IsLambdaBodyStatementOrExpression(node, body) AndAlso node Is body
End Function
''' <summary>
''' Returns true if the specified <paramref name="node"/> is a statement whose parent is a <see cref="LambdaExpressionSyntax"/>,
''' or and expression of a query lambda. Returns the node that represents the body of the lambda in <paramref name="body"/>.
''' </summary>
Public Shared Function IsLambdaBodyStatementOrExpression(node As SyntaxNode, <Out> Optional ByRef body As SyntaxNode = Nothing) As Boolean
Dim parent = node?.Parent
If parent Is Nothing Then
body = Nothing
Return False
End If
body = node
Select Case parent.Kind
Dim body1 As SyntaxNode = Nothing
Dim body2 As SyntaxNode = Nothing
Dim result = TryGetLambdaBodies(parent, body1, body2)
body = If(node Is body2, body2, body1)
Return result
End Function
Public Shared Function TryGetLambdaBodies(node As SyntaxNode, <Out> ByRef body1 As SyntaxNode, <Out> ByRef body2 As SyntaxNode) As Boolean
body1 = Nothing
body2 = Nothing
Select Case node.Kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
' The header of the lambda represents its body.
body = DirectCast(parent, LambdaExpressionSyntax).SubOrFunctionHeader
body1 = DirectCast(node, LambdaExpressionSyntax).SubOrFunctionHeader
Return True
Case SyntaxKind.WhereClause
Dim whereClause = DirectCast(parent, WhereClauseSyntax)
Return whereClause.Condition Is node
body1 = DirectCast(node, WhereClauseSyntax).Condition
Return True
' source sequence in From and Aggregate (other than the first in the query)
Case SyntaxKind.CollectionRangeVariable
Dim collectionRange = DirectCast(parent, CollectionRangeVariableSyntax)
Return collectionRange.Expression Is node AndAlso Not IsFirstInQuery(collectionRange)
Dim collectionRange = DirectCast(node, CollectionRangeVariableSyntax)
If IsFirstInQuery(collectionRange) Then
Return False
End If
body1 = collectionRange.Expression
Return True
' function call in Group By, Group Join, Aggregate: the argument
Case SyntaxKind.FunctionAggregation
Dim functionAggregation = DirectCast(parent, FunctionAggregationSyntax)
Return functionAggregation.Argument Is node
body1 = DirectCast(node, FunctionAggregationSyntax).Argument
Return True
' variable in Let, Select, Group By: the RHS
Case SyntaxKind.ExpressionRangeVariable
Dim expressionRange = DirectCast(parent, ExpressionRangeVariableSyntax)
Return expressionRange.Expression Is node
body1 = DirectCast(node, ExpressionRangeVariableSyntax).Expression
Return True
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Dim partitionWhileClause = DirectCast(parent, PartitionWhileClauseSyntax)
Return partitionWhileClause.Condition Is node
body1 = DirectCast(node, PartitionWhileClauseSyntax).Condition
Return True
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Dim ordering = DirectCast(parent, OrderingSyntax)
Return ordering.Expression Is node
body1 = DirectCast(node, OrderingSyntax).Expression
Return True
Case SyntaxKind.JoinCondition
Dim joinCondition = DirectCast(parent, JoinConditionSyntax)
Return joinCondition.Left Is node OrElse joinCondition.Right Is node
Dim joinCondition = DirectCast(node, JoinConditionSyntax)
body1 = joinCondition.Left
body2 = joinCondition.Right
Return True
End Select
Debug.Assert(Not SyntaxUtilities.IsLambda(parent.Kind))
Debug.Assert(Not IsLambda(node))
Return False
End Function
......@@ -192,6 +211,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return Not IsLambda(node.Kind())
End Function
Public Shared Function IsLambda(node As SyntaxNode) As Boolean
Return IsLambda(node.Kind())
End Function
Public Shared Function IsLambda(kind As SyntaxKind) As Boolean
Select Case kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
......
......@@ -375,10 +375,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Debug.Assert(node IsNot Nothing)
Dim lambdaBody As SyntaxNode = Nothing
While node IsNot declarationBody AndAlso
Not StatementSyntaxComparer.HasLabel(node.Kind) AndAlso
Not SyntaxUtilities.IsLambdaBodyStatement(node, lambdaBody)
Not SyntaxUtilities.IsLambdaBodyStatementOrExpression(node)
node = node.Parent
If partnerOpt IsNot Nothing Then
......@@ -408,7 +407,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
While node IsNot root
Dim body As SyntaxNode = Nothing
If SyntaxUtilities.IsLambdaBodyStatement(node, body) Then
If SyntaxUtilities.IsLambdaBodyStatementOrExpression(node, body) Then
Return body
End If
......@@ -880,6 +879,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return model.GetDeclaredSymbol(node, cancellationToken)
End Function
Friend Overrides Function ContainsLambda(declaration As SyntaxNode) As Boolean
Return declaration.DescendantNodes().Any(AddressOf SyntaxUtilities.IsLambda)
End Function
Friend Overrides Function IsLambda(node As SyntaxNode) As Boolean
Return SyntaxUtilities.IsLambda(node)
End Function
Friend Overrides Function TryGetLambdaBodies(node As SyntaxNode, ByRef body1 As SyntaxNode, ByRef body2 As SyntaxNode) As Boolean
Return SyntaxUtilities.TryGetLambdaBodies(node, body1, body2)
End Function
#End Region
#Region "Diagnostic Info"
......@@ -2720,8 +2730,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return node
End Select
Dim lambdaBody As SyntaxNode = Nothing
If SyntaxUtilities.IsLambdaBodyStatement(node, lambdaBody) Then
If SyntaxUtilities.IsLambdaBodyStatementOrExpression(node) Then
Return node
End If
......
......@@ -338,7 +338,7 @@ public TNode NewRoot
}
}
public IEnumerable<KeyValuePair<TNode, TNode>> Matches
public IReadOnlyDictionary<TNode, TNode> Matches
{
get
{
......@@ -346,6 +346,14 @@ public TNode NewRoot
}
}
public IReadOnlyDictionary<TNode, TNode> ReverseMatches
{
get
{
return new ReadOnlyDictionary<TNode, TNode>(_twoToOne);
}
}
public bool TryGetNewNode(TNode oldNode, out TNode newNode)
{
return _oneToTwo.TryGetValue(oldNode, out newNode);
......
......@@ -121,6 +121,7 @@ Microsoft.CodeAnalysis.Differencing.Match<TNode>.GetTreeEdits()
Microsoft.CodeAnalysis.Differencing.Match<TNode>.Matches.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.NewRoot.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.OldRoot.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.ReverseMatches.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.TryGetNewNode(TNode oldNode, out TNode newNode)
Microsoft.CodeAnalysis.Differencing.Match<TNode>.TryGetOldNode(TNode newNode, out TNode oldNode)
Microsoft.CodeAnalysis.Differencing.TreeComparer<TNode>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册