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,