提交 663d01ab 编写于 作者: T Tomas Matousek

Avoid dups in a map during analysis of captured lambda parameters

上级 93b320dc
......@@ -3812,6 +3812,56 @@ void F(int x1)
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem(2223, "https://github.com/dotnet/roslyn/issues/2223")]
public void Lambdas_Update_CapturedParameters2()
{
var src1 = @"
using System;
class C
{
void F(int x1)
{
var f1 = new Func<int, int, int>((a1, a2) =>
{
var f2 = new Func<int, int>(a3 => x1 + a2);
return a1;
});
var f3 = new Func<int, int, int>((a1, a2) =>
{
var f4 = new Func<int, int>(a3 => x1 + a2);
return a1;
});
}
}
";
var src2 = @"
using System;
class C
{
void F(int x1)
{
var f1 = new Func<int, int, int>((a1, a2) =>
{
var f2 = new Func<int, int>(a3 => x1 + a2 + 1);
return a1;
});
var f3 = new Func<int, int, int>((a1, a2) =>
{
var f4 = new Func<int, int>(a3 => x1 + a2 + 1);
return a1;
});
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Lambdas_Update_CeaseCapture_Closure1()
{
......
......@@ -3252,6 +3252,48 @@ End Class
edits.VerifySemanticDiagnostics()
End Sub
<Fact, WorkItem(2223, "https://github.com/dotnet/roslyn/issues/2223")>
Public Sub Lambdas_Update_CapturedParameters2()
Dim src1 = "
Imports System
Class C
Sub F(x1 As Integer)
Dim f1 = New Func(Of Integer, Integer, Integer)(
Function(a1, a2)
Dim f2 = New Func(Of Integer, Integer)(Function(a3) x1 + a2)
Return a1
End Function)
Dim f3 = New Func(Of Integer, Integer, Integer)(
Function(a1, a2)
Dim f4 = New Func(Of Integer, Integer)(Function(a3) x1 + a2)
Return a1
End Function)
End Sub
End Class
"
Dim src2 = "
Imports System
Class C
Sub F(x1 As Integer)
Dim f1 = New Func(Of Integer, Integer, Integer)(
Function(a1, a2)
Dim f2 = New Func(Of Integer, Integer)(Function(a3) x1 + a2 + 1)
Return a1
End Function)
Dim f3 = New Func(Of Integer, Integer, Integer)(
Function(a1, a2)
Dim f4 = New Func(Of Integer, Integer)(Function(a3) x1 + a2 + 1)
Return a1
End Function)
End Sub
End Class
"
Dim edits = GetTopEdits(src1, src2)
edits.VerifySemanticDiagnostics()
End Sub
<Fact>
Public Sub Lambdas_Update_CeaseCapture_Closure1()
Dim src1 = "
......@@ -3366,9 +3408,8 @@ End Class
"
Dim edits = GetTopEdits(src1, src2)
' TODO: better location
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.NotCapturingVariable, "F", "a1"))
Diagnostic(RudeEditKind.NotCapturingVariable, "a1", "a1"))
End Sub
<Fact>
......
......@@ -1241,6 +1241,25 @@ private static TextSpan GetDiagnosticSpan(SyntaxTokenList modifiers, SyntaxNodeO
return TextSpan.FromBounds((modifiers.Count != 0) ? modifiers.First().SpanStart : start.SpanStart, end.Span.End);
}
internal override TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, int ordinal)
{
switch (lambda.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
return ((ParenthesizedLambdaExpressionSyntax)lambda).ParameterList.Parameters[ordinal].Identifier.Span;
case SyntaxKind.SimpleLambdaExpression:
Debug.Assert(ordinal == 0);
return ((SimpleLambdaExpressionSyntax)lambda).Parameter.Identifier.Span;
case SyntaxKind.AnonymousMethodExpression:
return ((AnonymousMethodExpressionSyntax)lambda).ParameterList.Parameters[ordinal].Identifier.Span;
default:
return lambda.Span;
}
}
protected override string GetTopLevelDisplayName(SyntaxNode node, EditKind editKind)
{
return GetTopLevelDisplayNameImpl(node, editKind);
......
......@@ -216,6 +216,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
protected abstract IEnumerable<SyntaxNode> GetVariableUseSites(IEnumerable<SyntaxNode> roots, ISymbol localOrParameter, SemanticModel model, CancellationToken cancellationToken);
protected abstract TextSpan GetDiagnosticSpan(SyntaxNode node, EditKind editKind);
internal abstract TextSpan GetLambdaParameterDiagnosticSpan(SyntaxNode lambda, int ordinal);
protected abstract string GetTopLevelDisplayName(SyntaxNode node, EditKind editKind);
protected abstract string GetStatementDisplayName(SyntaxNode node, EditKind editKind);
protected abstract string GetLambdaDisplayName(SyntaxNode lambda);
......@@ -2968,6 +2969,45 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
}
}
private ValueTuple<SyntaxNode, int> GetParameterKey(IParameterSymbol parameter, CancellationToken cancellationToken)
{
var containingLambda = parameter.ContainingSymbol as IMethodSymbol;
if (containingLambda?.MethodKind == MethodKind.LambdaMethod)
{
var oldContainingLambdaSyntax = GetSymbolSyntax(containingLambda, cancellationToken);
return ValueTuple.Create(oldContainingLambdaSyntax, parameter.Ordinal);
}
else
{
return ValueTuple.Create(default(SyntaxNode), parameter.Ordinal);
}
}
private bool TryMapParameter(ValueTuple<SyntaxNode, int> parameterKey, IReadOnlyDictionary<SyntaxNode, SyntaxNode> map, out ValueTuple<SyntaxNode, int> mappedParameterKey)
{
SyntaxNode containingLambdaSyntax = parameterKey.Item1;
int ordinal = parameterKey.Item2;
if (containingLambdaSyntax == null)
{
// method parameter: no syntax, same ordinal (can't change since method signatures must match)
mappedParameterKey = parameterKey;
return true;
}
SyntaxNode mappedContainingLambdaSyntax;
if (map.TryGetValue(containingLambdaSyntax, out mappedContainingLambdaSyntax))
{
// parameter of an existing lambda: same ordinal (can't change since lambda signatures must match),
mappedParameterKey = ValueTuple.Create(mappedContainingLambdaSyntax, ordinal);
return true;
}
// no mapping
mappedParameterKey = default(ValueTuple<SyntaxNode, int>);
return false;
}
private void CalculateCapturedVariablesMaps(
SemanticModel oldModel,
ImmutableArray<ISymbol> oldCaptures,
......@@ -3017,17 +3057,19 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
// closure scopes in the new version to the previous ones, keeping empty closures around.
var oldLocalCapturesBySyntax = PooledDictionary<SyntaxNode, int>.GetInstance();
var oldParameterCapturesByOrdinal = PooledDictionary<int, int>.GetInstance();
var oldParameterCapturesByLambdaAndOrdinal = PooledDictionary<ValueTuple<SyntaxNode, int>, int>.GetInstance();
for (int i = 0; i < oldCaptures.Length; i++)
{
if (oldCaptures[i].Kind == SymbolKind.Parameter)
var oldCapture = oldCaptures[i];
if (oldCapture.Kind == SymbolKind.Parameter)
{
oldParameterCapturesByOrdinal.Add(((IParameterSymbol)oldCaptures[i]).Ordinal, i);
oldParameterCapturesByLambdaAndOrdinal.Add(GetParameterKey((IParameterSymbol)oldCapture, cancellationToken), i);
}
else
{
oldLocalCapturesBySyntax.Add(GetSymbolSyntax(oldCaptures[i], cancellationToken), i);
oldLocalCapturesBySyntax.Add(GetSymbolSyntax(oldCapture, cancellationToken), i);
}
}
......@@ -3039,11 +3081,11 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
if (newCapture.Kind == SymbolKind.Parameter)
{
var newParameterCapture = (IParameterSymbol)newCapture;
var newParameterKey = GetParameterKey(newParameterCapture, cancellationToken);
// parameters can't be reordered, deleted or added, so no syntax mapping is needed
int ordinal = newParameterCapture.Ordinal;
if (!oldParameterCapturesByOrdinal.TryGetValue(ordinal, out oldCaptureIndex))
ValueTuple<SyntaxNode, int> oldParameterKey;
if (!TryMapParameter(newParameterKey, map.Reverse, out oldParameterKey) ||
!oldParameterCapturesByLambdaAndOrdinal.TryGetValue(oldParameterKey, out oldCaptureIndex))
{
// parameter has not been captured prior the edit:
diagnostics.Add(new RudeEditDiagnostic(
......@@ -3053,11 +3095,12 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
new[] { newCapture.Name }));
hasErrors = true;
continue;
}
// Remove the old parameter capture so that at the end we can use this hashset
// to identify old captures that don't have a corresponding capture in the new version:
oldParameterCapturesByOrdinal.Remove(ordinal);
oldParameterCapturesByLambdaAndOrdinal.Remove(oldParameterKey);
}
else
{
......@@ -3152,14 +3195,15 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
// that have no corresponding captured variables in the new version.
// Report a rude edit for all such variables.
if (oldParameterCapturesByOrdinal.Count > 0)
if (oldParameterCapturesByLambdaAndOrdinal.Count > 0)
{
var newMemberParameters = GetParametersWithSyntax(newMember);
// uncaptured parameters:
foreach (var entry in oldParameterCapturesByOrdinal)
foreach (var entry in oldParameterCapturesByLambdaAndOrdinal)
{
int ordinal = entry.Key;
int ordinal = entry.Key.Item2;
var oldContainingLambdaSyntax = entry.Key.Item1;
int oldCaptureIndex = entry.Value;
var oldCapture = oldCaptures[oldCaptureIndex];
......@@ -3169,31 +3213,15 @@ private static ImmutableArray<IParameterSymbol> GetParametersWithSyntax(ISymbol
// this parameter:
span = GetThisParameterDiagnosticSpan(newMember);
}
else if (oldCapture.ContainingSymbol == oldMember)
else if (oldContainingLambdaSyntax != null)
{
// method or property:
span = GetVariableDiagnosticSpan(newMemberParameters[ordinal]);
// lambda:
span = GetLambdaParameterDiagnosticSpan(oldContainingLambdaSyntax, ordinal);
}
else
{
// lambda:
// We don't include lambda parameters in mapping, so we need to go thru symbols:
var oldCaptureSyntax = GetSymbolSyntax(oldCapture, cancellationToken);
var oldContainingLambda = (IMethodSymbol)oldModel.GetEnclosingSymbol(oldCaptureSyntax.SpanStart);
// TODO: VB doesn't return lambda symbol, but the containing method (bug https://github.com/dotnet/roslyn/issues/1290)
if (oldContainingLambda.MethodKind == MethodKind.LambdaMethod)
{
var oldContainingLambdaSyntax = GetSymbolSyntax(oldContainingLambda, cancellationToken);
var newContainingLambdaSyntax = map.Forward[oldContainingLambdaSyntax];
var newContainingLambda = (IMethodSymbol)newModel.GetEnclosingSymbol(newContainingLambdaSyntax.SpanStart);
span = GetVariableDiagnosticSpan(newContainingLambda.Parameters[ordinal]);
}
else
{
span = GetThisParameterDiagnosticSpan(newMember);
}
// method or property:
span = GetVariableDiagnosticSpan(newMemberParameters[ordinal]);
}
diagnostics.Add(new RudeEditDiagnostic(
......
......@@ -1351,6 +1351,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return TextSpan.FromBounds(startToken.SpanStart, endToken.Span.End)
End Function
Friend Overrides Function GetLambdaParameterDiagnosticSpan(lambda As SyntaxNode, ordinal As Integer) As TextSpan
Select Case lambda.Kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
Return DirectCast(lambda, LambdaExpressionSyntax).SubOrFunctionHeader.ParameterList.Parameters(ordinal).Identifier.Span
Case Else
Return lambda.Span
End Select
End Function
Protected Overrides Function GetTopLevelDisplayName(node As SyntaxNode, editKind As EditKind) As String
Return GetTopLevelDisplayNameImpl(node)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册