diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs
index 85635f69c5ced63964451a63f7c36fbd7a8d64da..6170655b8618e6d002f575ed640300ce6d4b385e 100644
--- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs
+++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs
@@ -7648,6 +7648,45 @@ static void Foo(int a)
Diagnostic(RudeEditKind.Delete, null, FeaturesResources.Class));
}
+ [Fact]
+ public void PartiallyExecutedActiveStatement()
+ {
+ string src1 = @"
+class C
+{
+ public static void F()
+ {
+ Console.WriteLine(1);
+ Console.WriteLine(2);
+ Console.WriteLine(3);
+ Console.WriteLine(4);
+ }
+}";
+ string src2 = @"
+class C
+{
+ public static void F()
+ {
+ Console.WriteLine(10);
+ Console.WriteLine(20);
+ Console.WriteLine(30);
+ Console.WriteLine(40);
+ }
+}";
+ var edits = GetTopEdits(src1, src2);
+ var active = GetActiveStatements(src1, src2);
+
+ active.OldSpans[0] = new ActiveStatementSpan(ActiveStatementFlags.PartiallyExecuted | ActiveStatementFlags.LeafFrame, active.OldSpans[0].Span);
+ active.OldSpans[1] = new ActiveStatementSpan(ActiveStatementFlags.PartiallyExecuted, active.OldSpans[1].Span);
+ active.OldSpans[2] = new ActiveStatementSpan(ActiveStatementFlags.LeafFrame, active.OldSpans[2].Span);
+ active.OldSpans[3] = new ActiveStatementSpan(ActiveStatementFlags.None, active.OldSpans[3].Span);
+
+ edits.VerifyRudeDiagnostics(active,
+ Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementUpdate, "Console.WriteLine(10);"),
+ Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(20);"),
+ Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(40);"));
+ }
+
#endregion
}
}
diff --git a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs
index ab40a6bc3947396b9c87b3d9e901b3f5d43b27eb..11b08469cc3cb889a0a396516a9c7aa8198b7d69 100644
--- a/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs
+++ b/src/EditorFeatures/Test/EditAndContinue/RudeEditDiagnosticTests.cs
@@ -22,6 +22,7 @@ public void ToDiagnostic()
var arg0 = new HashSet()
{
RudeEditKind.ActiveStatementUpdate,
+ RudeEditKind.PartiallyExecutedActiveStatementUpdate,
RudeEditKind.DeleteActiveStatement,
RudeEditKind.UpdateExceptionHandlerOfActiveTry,
RudeEditKind.UpdateTryOrCatchWithActiveFinally,
diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb
index 8ac46fc1657e31cc3de86cdc0fbe38ed37274e03..8929535166af54548b13fc89d5cf3ecc2e8e1dfe 100644
--- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb
+++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb
@@ -4975,8 +4975,44 @@ End Module"
Extensions.VerifyUnchangedDocument(src2, active)
End Sub
+
+
#End Region
+
+ Public Sub PartiallyExecutedActiveStatement()
+ Dim src1 As String = "
+Class C
+ Sub F()
+ Console.WriteLine(1)
+ Console.WriteLine(2)
+ Console.WriteLine(3)
+ Console.WriteLine(4)
+ End Sub
+End Class
+"
+ Dim src2 As String = "
+Class C
+ Sub F()
+ Console.WriteLine(10)
+ Console.WriteLine(20)
+ Console.WriteLine(30)
+ Console.WriteLine(40)
+ End Sub
+End Class
+"
+ Dim edits = GetTopEdits(src1, src2)
+ Dim active = GetActiveStatements(src1, src2)
+
+ active.OldSpans(0) = New ActiveStatementSpan(ActiveStatementFlags.PartiallyExecuted Or ActiveStatementFlags.LeafFrame, active.OldSpans(0).Span)
+ active.OldSpans(1) = New ActiveStatementSpan(ActiveStatementFlags.PartiallyExecuted, active.OldSpans(1).Span)
+ active.OldSpans(2) = New ActiveStatementSpan(ActiveStatementFlags.LeafFrame, active.OldSpans(2).Span)
+ active.OldSpans(3) = New ActiveStatementSpan(ActiveStatementFlags.None, active.OldSpans(3).Span)
+ edits.VerifyRudeDiagnostics(active,
+ Diagnostic(RudeEditKind.PartiallyExecutedActiveStatementUpdate, "Console.WriteLine(10)"),
+ Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(20)"),
+ Diagnostic(RudeEditKind.ActiveStatementUpdate, "Console.WriteLine(40)"))
+ End Sub
End Class
End Namespace
diff --git a/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs
index 528b71af8a6c3442d3375315991c8a5a49447e6a..e674c5e36dda8f9f39049d7b831dde3c20c672f5 100644
--- a/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs
+++ b/src/Features/Core/EditAndContinue/AbstractEditAndContinueAnalyzer.cs
@@ -991,6 +991,7 @@ internal struct UpdatedMemberInfo
int ordinal = start + i;
bool hasMatching = false;
bool isLeaf = (oldActiveStatements[ordinal].Flags & ActiveStatementFlags.LeafFrame) != 0;
+ bool isPartiallyExecuted = (oldActiveStatements[ordinal].Flags & ActiveStatementFlags.PartiallyExecuted) != 0;
int statementPart = activeNodes[i].StatementPart;
var oldStatementSyntax = activeNodes[i].OldNode;
var oldEnclosingLambdaBody = activeNodes[i].EnclosingLambdaBodyOpt;
@@ -1036,10 +1037,10 @@ internal struct UpdatedMemberInfo
// E.g. "const" keyword is inserted into a local variable declaration with an initializer.
newSpan = FindClosestActiveSpan(newStatementSyntax, statementPart);
- if (!isLeaf && !AreEquivalentActiveStatements(oldStatementSyntax, newStatementSyntax, statementPart))
+ if ((!isLeaf || isPartiallyExecuted) && !AreEquivalentActiveStatements(oldStatementSyntax, newStatementSyntax, statementPart))
{
// rude edit: internal active statement changed
- diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.ActiveStatementUpdate, newSpan));
+ diagnostics.Add(new RudeEditDiagnostic(isLeaf ? RudeEditKind.PartiallyExecutedActiveStatementUpdate : RudeEditKind.ActiveStatementUpdate, newSpan));
}
// exception handling around the statement:
diff --git a/src/Features/Core/EditAndContinue/ActiveStatementFlags.cs b/src/Features/Core/EditAndContinue/ActiveStatementFlags.cs
index ee870c9cd4ea275be3e13fe3695d018a610c2d5e..f80aec231cd914743842480a72d1d499e3fd53ed 100644
--- a/src/Features/Core/EditAndContinue/ActiveStatementFlags.cs
+++ b/src/Features/Core/EditAndContinue/ActiveStatementFlags.cs
@@ -17,6 +17,13 @@ internal enum ActiveStatementFlags
///
/// The statement is partially executed.
///
+ ///
+ /// An active statement is partially executed if the thread is stopped in between two sequence points.
+ /// This may happen when the users steps trhough the code in disassembly window (stepping over machine instructions),
+ /// when the compiler emits a call to Debugger.Break (VB Stop statement), etc.
+ ///
+ /// Partially executed active statement can't be edited.
+ ///
PartiallyExecuted = 2,
///
diff --git a/src/Features/Core/EditAndContinue/RudeEditDiagnosticDescriptors.cs b/src/Features/Core/EditAndContinue/RudeEditDiagnosticDescriptors.cs
index 284cbd9d526fa19c149ad6a50d60663e63adbc85..cb678124c6a6a398878ad07d8375aa9abd882267 100644
--- a/src/Features/Core/EditAndContinue/RudeEditDiagnosticDescriptors.cs
+++ b/src/Features/Core/EditAndContinue/RudeEditDiagnosticDescriptors.cs
@@ -75,6 +75,8 @@ internal static class RudeEditDiagnosticDescriptors
{ GetDescriptorPair(RudeEditKind.DeleteLambdaWithMultiScopeCapture, FeaturesResources.DeleteLambdaWithMultiScopeCapture) },
{ GetDescriptorPair(RudeEditKind.ActiveStatementUpdate, FeaturesResources.UpdatingAnActiveStatement) },
{ GetDescriptorPair(RudeEditKind.ActiveStatementLambdaRemoved, FeaturesResources.RemovingThatContainsActiveStatement) },
+ // TODO: change the error message to better explain what's going on
+ { GetDescriptorPair(RudeEditKind.PartiallyExecutedActiveStatementUpdate, FeaturesResources.UpdatingAnActiveStatement) },
{ GetDescriptorPair(RudeEditKind.InsertFile, FeaturesResources.AddingANewFile) },
{ GetDescriptorPair(RudeEditKind.RUDE_EDIT_COMPLEX_QUERY_EXPRESSION, FeaturesResources.ModifyingAWhichContainsComplexQuery) },
diff --git a/src/Features/Core/EditAndContinue/RudeEditKind.cs b/src/Features/Core/EditAndContinue/RudeEditKind.cs
index 52790f7dc3f100336f6e50bd64b6d499e508f0ce..ad5e5cd1232f89ce9d661efa093688319c3cc021 100644
--- a/src/Features/Core/EditAndContinue/RudeEditKind.cs
+++ b/src/Features/Core/EditAndContinue/RudeEditKind.cs
@@ -87,6 +87,7 @@ internal enum RudeEditKind : ushort
InsertHandlesClause = 70,
InsertFile = 71,
+ PartiallyExecutedActiveStatementUpdate = 72,
// TODO: remove values below
RUDE_EDIT_COMPLEX_QUERY_EXPRESSION = 0x103,