提交 8c7643c7 编写于 作者: T Tomas Matousek

Harden active statement mapping

上级 2714017c
......@@ -1719,18 +1719,18 @@ static void Main(string[] args)
string src2 = @"
class C
{
<AS:1>public static readonly int a = F(1);</AS:1>
<AS:1>public static readonly int <TS:1>a = F(1)</TS:1>;</AS:1>
public C() {}
public static int F(int a)
{
<AS:0>return a + 1;</AS:0>
<TS:0><AS:0>return a + 1;</AS:0></TS:0>
}
static void Main(string[] args)
{
<AS:2>C c = new C();</AS:2>
<TS:2><AS:2>C c = new C();</AS:2></TS:2>
}
}";
var edits = GetTopEdits(src1, src2);
......@@ -7957,6 +7957,52 @@ public static int F(int a)
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void MisplacedActiveStatement2()
{
string src1 = @"
class C
{
static <AS:0>void</AS:0> Main(string[] args)
{
}
}";
string src2 = @"
class C
{
static void Main(string[] args)
<AS:0>{</AS:0>
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
[Fact]
public void MisplacedTrackingSpan1()
{
string src1 = @"
class C
{
static <AS:0>void</AS:0> Main(string[] args)
{
}
}";
string src2 = @"
class C
{
static <TS:0>void</TS:0> Main(string[] args)
<AS:0>{</AS:0>
}
}";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active);
}
#endregion
#region Unmodified Documents
......
......@@ -19,7 +19,7 @@ internal static class Extensions
CSharpEditAndContinueTestHelpers.Instance.VerifyUnchangedDocument(
ActiveStatementsDescription.ClearTags(source),
description.OldSpans,
description.TrackingSpans,
description.OldTrackingSpans,
description.NewSpans,
description.OldRegions,
description.NewRegions);
......
......@@ -17,7 +17,7 @@ internal class ActiveStatementsDescription
public readonly TextSpan[] NewSpans;
public readonly ImmutableArray<TextSpan>[] OldRegions;
public readonly ImmutableArray<TextSpan>[] NewRegions;
public readonly TextSpan?[] TrackingSpans;
public readonly TextSpan?[] OldTrackingSpans;
private ActiveStatementsDescription()
{
......@@ -25,7 +25,7 @@ private ActiveStatementsDescription()
NewSpans = Array.Empty<TextSpan>();
OldRegions = Array.Empty<ImmutableArray<TextSpan>>();
NewRegions = Array.Empty<ImmutableArray<TextSpan>>();
TrackingSpans = null;
OldTrackingSpans = null;
}
public ActiveStatementsDescription(string oldSource, string newSource)
......@@ -37,7 +37,9 @@ public ActiveStatementsDescription(string oldSource, string newSource)
// Tracking spans are marked in the new source since the editor moves them around as the user
// edits the source and we get their positions when analyzing the new source.
TrackingSpans = GetTrackingSpans(newSource, OldSpans.Length);
// The EnC analyzer uses old trackign spans as hints to find matching nodes.
// After an edit the tracking spans are updated to match new active statements.
OldTrackingSpans = GetTrackingSpans(newSource, OldSpans.Length);
}
internal static readonly ActiveStatementsDescription Empty = new ActiveStatementsDescription();
......
......@@ -80,9 +80,9 @@ internal abstract class EditAndContinueTestHelpers
{
var oldActiveStatements = description.OldSpans;
if (description.TrackingSpans != null)
if (description.OldTrackingSpans != null)
{
Assert.Equal(oldActiveStatements.Length, description.TrackingSpans.Length);
Assert.Equal(oldActiveStatements.Length, description.OldTrackingSpans.Length);
}
string newSource = editScript.Match.NewRoot.SyntaxTree.ToString();
......@@ -100,9 +100,9 @@ internal abstract class EditAndContinueTestHelpers
DocumentId documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument");
TestActiveStatementTrackingService trackingService;
if (description.TrackingSpans != null)
if (description.OldTrackingSpans != null)
{
trackingService = new TestActiveStatementTrackingService(documentId, description.TrackingSpans);
trackingService = new TestActiveStatementTrackingService(documentId, description.OldTrackingSpans);
}
else
{
......@@ -156,8 +156,9 @@ internal abstract class EditAndContinueTestHelpers
}
}
if (description.TrackingSpans != null)
if (description.OldTrackingSpans != null)
{
// Verify that the new tracking spans are equal to the new active statements.
AssertEx.Equal(trackingService.TrackingSpans, description.NewSpans.Select(s => (TextSpan?)s));
}
}
......
......@@ -4749,6 +4749,94 @@ End Class
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact>
Public Sub MisplacedActiveStatement2()
Dim src1 = "
Class C
<AS:0><Attr></AS:0>
Shared Sub Main()
End Sub
End Class"
Dim src2 = "
Class C
<Attr>
<AS:0>Shared Sub Main()</AS:0>
End Sub
End Class"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact>
Public Sub MisplacedTrackingSpan1()
Dim src1 = "
Class C
<AS:0><Attr></AS:0>
Shared Sub Main()
End Sub
End Class"
Dim src2 = "
Class C
<TS:0><Attr></TS:0>
<AS:0>Shared Sub Main()</AS:0>
End Sub
End Class"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact>
Public Sub MisplacedTrackingSpan2()
Dim src1 = "
Class C
Dim f = <AS:0>1</AS:0>
End Class"
Dim src2 = "
Class C
<TS:0>Dim</TS:0> <AS:0>f = 1</AS:0>
End Class"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact>
Public Sub MisplacedTrackingSpan3()
Dim src1 = "
Class C
Dim <AS:0>f</AS:0>, g As New C()
End Class"
Dim src2 = "
Class C
<TS:0>Dim</TS:0> <AS:0>f</AS:0>, g As New C()
End Class"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact>
Public Sub MisplacedTrackingSpan4()
Dim src1 = "
Class C
<AS:0>Property</AS:0> f As New C()
End Class"
Dim src2 = "
Class C
<TS:0>Property</TS:0> <AS:0>f As New C()</AS:0>
End Class"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active)
End Sub
<Fact, WorkItem(1359, "https://github.com/dotnet/roslyn/issues/1359")>
Public Sub Lambdas_LeafEdits_GeneralStatement()
Dim src1 = "
......
......@@ -20,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests
VisualBasicEditAndContinueTestHelpers.Instance.VerifyUnchangedDocument(
ActiveStatementsDescription.ClearTags(source),
description.OldSpans,
description.TrackingSpans,
description.OldTrackingSpans,
description.NewSpans,
description.OldRegions,
description.NewRegions)
......
......@@ -270,13 +270,12 @@ protected override SyntaxNode FindStatementAndPartner(SyntaxNode declarationBody
declarationBody = constructor.Initializer;
partnerDeclarationBodyOpt = partnerConstructor?.Initializer;
}
else
{
Debug.Assert(!(declarationBody is BlockSyntax));
}
// let's find a labeled node that encompasses the body:
position = declarationBody.SpanStart;
}
if (!declarationBody.FullSpan.Contains(position))
{
// invalid position, let's find a labeled node that encompasses the body:
position = declarationBody.SpanStart;
}
SyntaxNode node;
......@@ -284,7 +283,7 @@ protected override SyntaxNode FindStatementAndPartner(SyntaxNode declarationBody
{
SyntaxUtilities.FindLeafNodeAndPartner(declarationBody, position, partnerDeclarationBodyOpt, out node, out partnerOpt);
}
else
else
{
node = declarationBody.FindToken(position).Parent;
partnerOpt = null;
......@@ -453,7 +452,7 @@ protected override SyntaxNode FindEnclosingLambdaBody(SyntaxNode containerOpt, S
{
SyntaxNode root = GetEncompassingAncestor(containerOpt);
while (node != root)
while (node != root && node != null)
{
SyntaxNode body;
if (LambdaUtilities.IsLambdaBodyStatementOrExpression(node, out body))
......
......@@ -123,6 +123,7 @@ protected SyntaxNode GetEncompassingAncestor(SyntaxNode bodyOrMatchRootOpt)
/// <remarks>
/// The declaration body node may not contain the <paramref name="position"/>.
/// This happens when an active statement associated with the member is outside of its body (e.g. C# constructor).
/// If the position doesn't correspond to any statement uses the start of the <paramref name="declarationBody"/>.
/// </remarks>
protected abstract SyntaxNode FindStatementAndPartner(SyntaxNode declarationBody, int position, SyntaxNode partnerDeclarationBodyOpt, out SyntaxNode partnerOpt, out int statementPart);
......@@ -506,12 +507,26 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
lineEdits.AsImmutable(),
hasSemanticErrors: false);
}
catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
catch (Exception e) when (ReportFatalErrorAnalyzeDocumentAsync(baseActiveStatements, e))
{
throw ExceptionUtilities.Unreachable;
}
}
// Active statements spans are usually unavailable in crash dumps due to a bug in the debugger (DevDiv #150901),
// so we stash them here in plain array (can't use immutable, see the bug) just before we report NFW.
private static ActiveStatementSpan[] s_fatalErrorBaseActiveStatements;
private static bool ReportFatalErrorAnalyzeDocumentAsync(ImmutableArray<ActiveStatementSpan> baseActiveStatements, Exception e)
{
if (!(e is OperationCanceledException))
{
s_fatalErrorBaseActiveStatements = baseActiveStatements.ToArray();
}
return FatalError.ReportUnlessCanceled(e);
}
internal Dictionary<SyntaxNode, EditKind> BuildEditMap(EditScript<SyntaxNode> editScript)
{
var map = new Dictionary<SyntaxNode, EditKind>(editScript.Edits.Length);
......@@ -598,7 +613,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
{
if (!editedActiveStatements[i])
{
Debug.Assert(newExceptionRegions[i].IsDefault);
Contract.ThrowIfFalse(newExceptionRegions[i].IsDefault);
TextSpan trackedSpan = default(TextSpan);
bool isTracked = trackingService != null &&
......@@ -624,7 +639,7 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
SyntaxNode newMember;
bool hasPartner = topMatch.TryGetNewNode(oldMember, out newMember);
Debug.Assert(hasPartner);
Contract.ThrowIfFalse(hasPartner);
SyntaxNode oldBody = TryGetDeclarationBody(oldMember, isMember: true);
SyntaxNode newBody = TryGetDeclarationBody(newMember, isMember: true);
......@@ -645,11 +660,18 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
if (isTracked && trackedSpan.Length != 0 && newMember.Span.Contains(trackedSpan))
{
int trackedStatementPart;
var trackedStatement = FindStatement(newBody, trackedSpan.Start, out trackedStatementPart);
Contract.ThrowIfNull(trackedStatement);
// Adjust for active statements that cover more than the old member span.
// For example, C# variable declarators that represent field initializers:
// [|public int <<F = Expr()>>;|]
int adjustedOldStatementStart = oldMember.FullSpan.Contains(oldStatementSpan.Start) ? oldStatementSpan.Start : oldMember.SpanStart;
// In rare cases the tracking span might have been moved outside of lambda.
// The tracking span might have been moved outside of lambda.
// It is not an error to move the statement - we just ignore it.
var oldEnclosingLambdaBody = FindEnclosingLambdaBody(oldBody, oldMember.FindToken(oldStatementSpan.Start).Parent);
var oldEnclosingLambdaBody = FindEnclosingLambdaBody(oldBody, oldMember.FindToken(adjustedOldStatementStart).Parent);
var newEnclosingLambdaBody = FindEnclosingLambdaBody(newBody, trackedStatement);
if (oldEnclosingLambdaBody == newEnclosingLambdaBody)
{
......@@ -660,8 +682,9 @@ private SyntaxNode FindStatement(SyntaxNode declarationBody, int position, out i
if (newStatement == null)
{
Debug.Assert(statementPart == -1);
Contract.ThrowIfFalse(statementPart == -1);
FindStatementAndPartner(oldBody, oldStatementSpan.Start, newBody, out newStatement, out statementPart);
Contract.ThrowIfNull(newStatement);
}
if (diagnostics.Count == 0)
......@@ -940,7 +963,10 @@ internal struct UpdatedMemberInfo
int statementPart;
var oldStatementStart = oldText.Lines.GetTextSpan(oldActiveStatements[ordinal].Span).Start;
var oldStatementSyntax = FindStatement(oldBody, oldStatementStart, out statementPart);
Contract.ThrowIfNull(oldStatementSyntax);
var oldEnclosingLambdaBody = FindEnclosingLambdaBody(oldBody, oldStatementSyntax);
if (oldEnclosingLambdaBody != null)
......@@ -975,6 +1001,8 @@ internal struct UpdatedMemberInfo
{
int part;
var newStatementSyntax = FindStatement(newBody, trackedSpan.Start, out part);
Contract.ThrowIfNull(newStatementSyntax);
var newEnclosingLambdaBody = FindEnclosingLambdaBody(newBody, newStatementSyntax);
// The tracking span might have been moved outside of the lambda span.
......
......@@ -418,18 +418,22 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
End If
End If
Debug.Assert(declarationBody.Parent.IsKind(SyntaxKind.EqualsValue))
Debug.Assert(declarationBody.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator) AndAlso
declarationBody.Parent.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration))
If declarationBody.Parent.IsKind(SyntaxKind.EqualsValue) Then
Debug.Assert(declarationBody.Parent.Parent.IsKind(SyntaxKind.VariableDeclarator) AndAlso
declarationBody.Parent.Parent.Parent.IsKind(SyntaxKind.FieldDeclaration))
If partnerDeclarationBodyOpt IsNot Nothing Then
partnerOpt = partnerDeclarationBodyOpt.Parent.Parent
End If
If partnerDeclarationBodyOpt IsNot Nothing Then
partnerOpt = partnerDeclarationBodyOpt.Parent.Parent
End If
Return declarationBody.Parent.Parent
Return declarationBody.Parent.Parent
End If
End If
Debug.Assert(declarationBody.FullSpan.Contains(position))
If Not declarationBody.FullSpan.Contains(position) Then
' invalid position, let's find a labeled node that encompasses the body:
position = declarationBody.SpanStart
End If
Dim node As SyntaxNode = Nothing
If partnerDeclarationBodyOpt IsNot Nothing Then
......@@ -544,7 +548,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Protected Overrides Function FindEnclosingLambdaBody(containerOpt As SyntaxNode, node As SyntaxNode) As SyntaxNode
Dim root As SyntaxNode = GetEncompassingAncestor(containerOpt)
While node IsNot root
While node IsNot root And node IsNot Nothing
Dim body As SyntaxNode = Nothing
If LambdaUtilities.IsLambdaBodyStatementOrExpression(node, body) Then
Return body
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册